Is it possible in the context of a browser extension to create a new window in mobile state

I'm working on a browser extension (specifically for Firefox), one of the features is that the extension should open a link in a new browser window so to have a preview of the target page. The window should be rather small and is just created like this:

 browser.windows
            .create({
                url: message.url,
                width: 400,
                height: 600,
                type: "popup",
                incognito: false,
            })
            .then((w) =>...

The problem I am having is that the result is the page loads in a new window and some target pages are shown with overflown content despite them being optimized for small screens. If I open such pages in responsive design mode (Ctrl+Shift+M for Firefox) the layout changes to the correct one (optimized for small screens).

You can check this behavior with google search results page for example:

  1. Try manually resizing the browser window with the results page and you will see the slider in the bottom at some point and some parts of the page overflown
  2. Open devtools and check if the page has the layout for small screens like mobile ones (or press Ctrl+Shift+M for Firefox). As expected Google has a nice layout for this case.

I suppose that pages like these are checking screen size of a user and user agent header rather than just inner width of the actual window or something like this.

So, is there a way to create a new window in a context of a browser extension and force it to behave like mobile window or maybe instruct it to replace the user agent header on requests or any other method to display such pages on a desktop nicely in a small window with the corresponding layout?

I have already checked for windows.create method reference, window and tab API references, there doesn't seem to be a way to do something like this. I tried to update the meta viewport tag on the target page, or resizing the window programmatically to no avail. The screen size is the readonly prop and I don't seem to find the means to replace it in runtime either.

Upvotes: 0

Views: 61

Answers (1)

I have found a solution which works for me. For anyone stumbling upon similar problem there is an option to use

webRequest.onBeforeSendHeaders

event if the extension is using Manifest v2:

  1. Intercept requests coming from the tab which is used to open the target URL

  2. Replace user-agent header with some which is a mobile agent to make sure the tab gets responses as if it was open on a mobile device

            const interceptor = (
                    details: browser.WebRequest.OnBeforeSendHeadersDetailsType,
                ) => {
                    if (details.requestHeaders) {
                        for (const header of details.requestHeaders) {
                            if (
                                header.name.toLowerCase() === "user-agent"
                            ) {
                                header.value =
                                    "Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1";
                            }
                        }
                        return {
                            requestHeaders: details.requestHeaders,
                        };
                    }
                };
                // register the interceptor callback only if necessary
                if (
                    !browser.webRequest.onBeforeSendHeaders.hasListener(
                        interceptor,
                    )
                ) {
                    browser.webRequest.onBeforeSendHeaders.addListener(
                        interceptor,
                        { urls: ["<all_urls>"], tabId: tab.id },
                        ["blocking", "requestHeaders"],
                    );
                }
                // remove the listener when needed
                // browser.webRequest.onBeforeSendHeaders.removeListener(
                //     interceptor,
                // );
    

To use the webRequest API for a given host, an extension must have the "webRequest" API permission and the host permission for that host. To use the "blocking" feature, the extension must also have the "webRequestBlocking" API permission. See for more details

The API is available to use in the background script.

For extensions with Manifest v3 there is an option to use

declarativeNetRequest.updateSessionRules

This one needs the "declarativeNetRequest" or "declarativeNetRequestWithHostAccess" permission in the manifest.json file.

An example of usage:

browser.declarativeNetRequest.updateSessionRules({
            addRules: [
                {
                    id: tabId,
                    priority: 1,
                    action: {
                        type: "modifyHeaders",
                        requestHeaders: [
                            {
                                header: "User-Agent",
                                operation: "set",
                                value: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1",
                            },
                        ],
                    },
                    condition: {
                        tabIds: [tabId],
                        resourceTypes: [
                            "main_frame",
                            "sub_frame",
                            "script",
                            "stylesheet",
                            "image",
                            "object",
                            "xmlhttprequest",
                            "other",
                            "websocket",
                            "media",
                            "font",
                        ],
                    },
                },
            ],
            removeRuleIds: [tabId],
        });

See more details here

Upvotes: 0

Related Questions