Chrome/FF/Safari extension: Load hidden web page i

2019-06-14 14:01发布

问题:

Is it possible to build an 'incognito mode' for loading background web-pages in a browser extension?

I am writing a non-IE cross-browser extension that periodically checks web-pages on the user's behalf. There are two requirements:

  1. Page checks are done in the background, to be as unobtrusive as possible. I believe this could be done by opening the page in a new unfocussed browser tab, or hidden in a sandboxed iframe in the extension's background page.
  2. The page checks should operate in 'incognito mode', and not use/update the user's cookies, history, or local storage. This is to stop the checks polluting the user's actual browsing behavior as much as possible.

Any thoughts on how to implement this 'incognito mode'?

It would ideally work in as many browser types as possible (not IE).

My current ideas are:

  • Filter out cookie headers from incoming/outgoing http requests associated with the page checks (if I can identify all of these) (not possible in Safari?)
  • After each page check, filter out the page from the user's history.

Useful SO questions I've found:

  • Chrome extension: loading a hidden page (without iframe)
  • Firefox addon development, open a hidden web browser
  • Identify requests originating in the hiddenDOMWindow (or one of its iframes)

回答1:

var Cu = Components.utils;
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/devtools/Console.jsm');
var win = Services.appShell.hiddenDOMWindow
var iframe = win.document.createElementNS('http://www.w3.org/1999/xhtml', 'iframe');
iframe.addEventListener('DOMContentLoaded', function(e) {
    var win = e.originalTarget.defaultView;
    console.log('done loaded', e.document.location);
    if (win.frameElement && win.frameElement != iframe) {
            //its a frame in the in iframe that load
    }
}, false);

win.document.documentElement.appendChild(iframe);

must keep a global var reference to iframe we added. then you can change the iframe location like this, and when its loaded it triggers the event listener above

iframe.contentWindow.location = 'http://www.bing.com/'

that DOMContentLoaded identifies all things loaded in that iframe. if the page has frames it detects that too.

to remove from history, into the DOMContentLoaded function use the history service to remove win.location from history: https://developer.mozilla.org/en-US/docs/Using_the_Places_history_service

now to strip the cookies from requests in that page use this code:

const {classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components;
Cu.import('resource://gre/modules/Services.jsm');

var myTabToSpoofIn = Services.wm.getMostRecentBrowser('navigator:browser').gBrowser.tabContainer[0]; //will spoof in the first tab of your browser

var httpRequestObserver = {
    observe: function (subject, topic, data) {
        var httpChannel, requestURL;

        if (topic == "http-on-modify-request") {
            httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
            var goodies = loadContextGoodies(httpChannel)
            if (goodies) {
              if (goodies.contentWindow.top == iframe.contentWindow.top) {
                httpChannel.setRequestHeader('Cookie', '', false);
              } else {
                //this page load isnt in our iframe so ignore it
              }
            }
        }
    }
};

Services.obs.addObserver(httpRequestObserver, "http-on-modify-request", false);
//Services.obs.removeObserver(httpRequestObserver, "http-on-modify-request", false); //run this on shudown of your addon otherwise the observer stags registerd







//this function gets the contentWindow and other good stuff from loadContext of httpChannel
function loadContextGoodies(httpChannel) {
    //httpChannel must be the subject of http-on-modify-request QI'ed to nsiHTTPChannel as is done on line 8 "httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);"
    //start loadContext stuff
    var loadContext;
    try {
        var interfaceRequestor = httpChannel.notificationCallbacks.QueryInterface(Ci.nsIInterfaceRequestor);
        //var DOMWindow = interfaceRequestor.getInterface(Components.interfaces.nsIDOMWindow); //not to be done anymore because: https://developer.mozilla.org/en-US/docs/Updating_extensions_for_Firefox_3.5#Getting_a_load_context_from_a_request //instead do the loadContext stuff below
        try {
            loadContext = interfaceRequestor.getInterface(Ci.nsILoadContext);
        } catch (ex) {
            try {
                loadContext = subject.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
            } catch (ex2) {}
        }
    } catch (ex0) {}

if (!loadContext) {
    //no load context so dont do anything although you can run this, which is your old code
    //this probably means that its loading an ajax call or like a google ad thing
    return null;
} else {
    var contentWindow = loadContext.associatedWindow;
    if (!contentWindow) {
        //this channel does not have a window, its probably loading a resource
        //this probably means that its loading an ajax call or like a google ad thing
        return null;
    } else {
        var aDOMWindow = contentWindow.top.QueryInterface(Ci.nsIInterfaceRequestor)
            .getInterface(Ci.nsIWebNavigation)
            .QueryInterface(Ci.nsIDocShellTreeItem)
            .rootTreeItem
            .QueryInterface(Ci.nsIInterfaceRequestor)
            .getInterface(Ci.nsIDOMWindow);
        var gBrowser = aDOMWindow.gBrowser;
        var aTab = gBrowser._getTabForContentWindow(contentWindow.top); //this is the clickable tab xul element, the one found in the tab strip of the firefox window, aTab.linkedBrowser is same as browser var above //can stylize tab like aTab.style.backgroundColor = 'blue'; //can stylize the tab like aTab.style.fontColor = 'red';
        var browser = aTab.linkedBrowser; //this is the browser within the tab //this is where the example in the previous section ends
        return {
            aDOMWindow: aDOMWindow,
            gBrowser: gBrowser,
            aTab: aTab,
            browser: browser,
            contentWindow: contentWindow
        };
    }
}
//end loadContext stuff

}