browser/base/content/content.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 let Cc = Components.classes;
     7 let Ci = Components.interfaces;
     8 let Cu = Components.utils;
    10 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    11 Cu.import("resource://gre/modules/Services.jsm");
    13 XPCOMUtils.defineLazyModuleGetter(this, "ContentLinkHandler",
    14   "resource:///modules/ContentLinkHandler.jsm");
    15 XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector",
    16   "resource:///modules/translation/LanguageDetector.jsm");
    17 XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
    18   "resource://gre/modules/LoginManagerContent.jsm");
    19 XPCOMUtils.defineLazyModuleGetter(this, "InsecurePasswordUtils",
    20   "resource://gre/modules/InsecurePasswordUtils.jsm");
    21 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
    22   "resource://gre/modules/PrivateBrowsingUtils.jsm");
    23 XPCOMUtils.defineLazyModuleGetter(this, "UITour",
    24   "resource:///modules/UITour.jsm");
    26 // Creates a new nsIURI object.
    27 function makeURI(uri, originCharset, baseURI) {
    28   return Services.io.newURI(uri, originCharset, baseURI);
    29 }
    31 addMessageListener("Browser:HideSessionRestoreButton", function (message) {
    32   // Hide session restore button on about:home
    33   let doc = content.document;
    34   let container;
    35   if (doc.documentURI.toLowerCase() == "about:home" &&
    36       (container = doc.getElementById("sessionRestoreContainer"))){
    37     container.hidden = true;
    38   }
    39 });
    41 if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
    42   addEventListener("contextmenu", function (event) {
    43     sendAsyncMessage("contextmenu", {}, { event: event });
    44   }, false);
    45 } else {
    46   addEventListener("DOMFormHasPassword", function(event) {
    47     InsecurePasswordUtils.checkForInsecurePasswords(event.target);
    48     LoginManagerContent.onFormPassword(event);
    49   });
    50   addEventListener("DOMAutoComplete", function(event) {
    51     LoginManagerContent.onUsernameInput(event);
    52   });
    53   addEventListener("blur", function(event) {
    54     LoginManagerContent.onUsernameInput(event);
    55   });
    57   addEventListener("mozUITour", function(event) {
    58     if (!Services.prefs.getBoolPref("browser.uitour.enabled"))
    59       return;
    61     let handled = UITour.onPageEvent(event);
    62     if (handled)
    63       addEventListener("pagehide", UITour);
    64   }, false, true);
    65 }
    67 let AboutHomeListener = {
    68   init: function(chromeGlobal) {
    69     chromeGlobal.addEventListener('AboutHomeLoad', () => this.onPageLoad(), false, true);
    70   },
    72   handleEvent: function(aEvent) {
    73     switch (aEvent.type) {
    74       case "AboutHomeLoad":
    75         this.onPageLoad();
    76         break;
    77     }
    78   },
    80   receiveMessage: function(aMessage) {
    81     switch (aMessage.name) {
    82       case "AboutHome:Update":
    83         this.onUpdate(aMessage.data);
    84         break;
    85     }
    86   },
    88   onUpdate: function(aData) {
    89     let doc = content.document;
    90     if (doc.documentURI.toLowerCase() != "about:home")
    91       return;
    93     if (aData.showRestoreLastSession && !PrivateBrowsingUtils.isWindowPrivate(content))
    94       doc.getElementById("launcher").setAttribute("session", "true");
    96     // Inject search engine and snippets URL.
    97     let docElt = doc.documentElement;
    98     // set the following attributes BEFORE searchEngineName, which triggers to
    99     // show the snippets when it's set.
   100     docElt.setAttribute("snippetsURL", aData.snippetsURL);
   101     if (aData.showKnowYourRights)
   102       docElt.setAttribute("showKnowYourRights", "true");
   103     docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
   104     docElt.setAttribute("searchEngineName", aData.defaultEngineName);
   105   },
   107   onPageLoad: function() {
   108     let doc = content.document;
   109     if (doc.documentURI.toLowerCase() != "about:home" ||
   110         doc.documentElement.hasAttribute("hasBrowserHandlers")) {
   111       return;
   112     }
   114     doc.documentElement.setAttribute("hasBrowserHandlers", "true");
   115     let self = this;
   116     addMessageListener("AboutHome:Update", self);
   117     addEventListener("click", this.onClick, true);
   118     addEventListener("pagehide", function onPageHide(event) {
   119       if (event.target.defaultView.frameElement)
   120         return;
   121       removeMessageListener("AboutHome:Update", self);
   122       removeEventListener("click", self.onClick, true);
   123       removeEventListener("pagehide", onPageHide, true);
   124       if (event.target.documentElement)
   125         event.target.documentElement.removeAttribute("hasBrowserHandlers");
   126     }, true);
   128     // XXX bug 738646 - when Marketplace is launched, remove this statement and
   129     // the hidden attribute set on the apps button in aboutHome.xhtml
   130     if (Services.prefs.getPrefType("browser.aboutHome.apps") == Services.prefs.PREF_BOOL &&
   131         Services.prefs.getBoolPref("browser.aboutHome.apps"))
   132       doc.getElementById("apps").removeAttribute("hidden");
   134     sendAsyncMessage("AboutHome:RequestUpdate");
   136     doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
   137       sendAsyncMessage("AboutHome:Search", { searchData: e.detail });
   138     }, true, true);
   139   },
   141   onClick: function(aEvent) {
   142     if (!aEvent.isTrusted || // Don't trust synthetic events
   143         aEvent.button == 2 || aEvent.target.localName != "button") {
   144       return;
   145     }
   147     let originalTarget = aEvent.originalTarget;
   148     let ownerDoc = originalTarget.ownerDocument;
   149     if (ownerDoc.documentURI != "about:home") {
   150       // This shouldn't happen, but we're being defensive.
   151       return;
   152     }
   154     let elmId = originalTarget.getAttribute("id");
   156     switch (elmId) {
   157       case "restorePreviousSession":
   158         sendAsyncMessage("AboutHome:RestorePreviousSession");
   159         ownerDoc.getElementById("launcher").removeAttribute("session");
   160         break;
   162       case "downloads":
   163         sendAsyncMessage("AboutHome:Downloads");
   164         break;
   166       case "bookmarks":
   167         sendAsyncMessage("AboutHome:Bookmarks");
   168         break;
   170       case "history":
   171         sendAsyncMessage("AboutHome:History");
   172         break;
   174       case "apps":
   175         sendAsyncMessage("AboutHome:Apps");
   176         break;
   178       case "addons":
   179         sendAsyncMessage("AboutHome:Addons");
   180         break;
   182       case "sync":
   183         sendAsyncMessage("AboutHome:Sync");
   184         break;
   186       case "settings":
   187         sendAsyncMessage("AboutHome:Settings");
   188         break;
   189     }
   190   },
   191 };
   192 AboutHomeListener.init(this);
   195 let ContentSearchMediator = {
   197   whitelist: new Set([
   198     "about:newtab",
   199   ]),
   201   init: function (chromeGlobal) {
   202     chromeGlobal.addEventListener("ContentSearchClient", this, true, true);
   203     addMessageListener("ContentSearch", this);
   204   },
   206   handleEvent: function (event) {
   207     if (this._contentWhitelisted) {
   208       this._sendMsg(event.detail.type, event.detail.data);
   209     }
   210   },
   212   receiveMessage: function (msg) {
   213     if (msg.data.type == "AddToWhitelist") {
   214       for (let uri of msg.data.data) {
   215         this.whitelist.add(uri);
   216       }
   217       this._sendMsg("AddToWhitelistAck");
   218       return;
   219     }
   220     if (this._contentWhitelisted) {
   221       this._fireEvent(msg.data.type, msg.data.data);
   222     }
   223   },
   225   get _contentWhitelisted() {
   226     return this.whitelist.has(content.document.documentURI.toLowerCase());
   227   },
   229   _sendMsg: function (type, data=null) {
   230     sendAsyncMessage("ContentSearch", {
   231       type: type,
   232       data: data,
   233     });
   234   },
   236   _fireEvent: function (type, data=null) {
   237     content.dispatchEvent(new content.CustomEvent("ContentSearchService", {
   238       detail: {
   239         type: type,
   240         data: data,
   241       },
   242     }));
   243   },
   244 };
   245 ContentSearchMediator.init(this);
   248 var global = this;
   250 // Lazily load the finder code
   251 addMessageListener("Finder:Initialize", function () {
   252   let {RemoteFinderListener} = Cu.import("resource://gre/modules/RemoteFinder.jsm", {});
   253   new RemoteFinderListener(global);
   254 });
   257 let ClickEventHandler = {
   258   init: function init() {
   259     Cc["@mozilla.org/eventlistenerservice;1"]
   260       .getService(Ci.nsIEventListenerService)
   261       .addSystemEventListener(global, "click", this, true);
   262   },
   264   handleEvent: function(event) {
   265     // Bug 903016: Most of this code is an unfortunate duplication from
   266     // contentAreaClick in browser.js.
   267     if (!event.isTrusted || event.defaultPrevented || event.button == 2)
   268       return;
   270     let [href, node] = this._hrefAndLinkNodeForClickEvent(event);
   272     let json = { button: event.button, shiftKey: event.shiftKey,
   273                  ctrlKey: event.ctrlKey, metaKey: event.metaKey,
   274                  altKey: event.altKey, href: null, title: null,
   275                  bookmark: false };
   277     if (href) {
   278       json.href = href;
   279       if (node) {
   280         json.title = node.getAttribute("title");
   282         if (event.button == 0 && !event.ctrlKey && !event.shiftKey &&
   283             !event.altKey && !event.metaKey) {
   284           json.bookmark = node.getAttribute("rel") == "sidebar";
   285           if (json.bookmark)
   286             event.preventDefault(); // Need to prevent the pageload.
   287         }
   288       }
   290       sendAsyncMessage("Content:Click", json);
   291       return;
   292     }
   294     // This might be middle mouse navigation.
   295     if (event.button == 1)
   296       sendAsyncMessage("Content:Click", json);
   297   },
   299   /**
   300    * Extracts linkNode and href for the current click target.
   301    *
   302    * @param event
   303    *        The click event.
   304    * @return [href, linkNode].
   305    *
   306    * @note linkNode will be null if the click wasn't on an anchor
   307    *       element (or XLink).
   308    */
   309   _hrefAndLinkNodeForClickEvent: function(event) {
   310     function isHTMLLink(aNode) {
   311       // Be consistent with what nsContextMenu.js does.
   312       return ((aNode instanceof content.HTMLAnchorElement && aNode.href) ||
   313               (aNode instanceof content.HTMLAreaElement && aNode.href) ||
   314               aNode instanceof content.HTMLLinkElement);
   315     }
   317     let node = event.target;
   318     while (node && !isHTMLLink(node)) {
   319       node = node.parentNode;
   320     }
   322     if (node)
   323       return [node.href, node];
   325     // If there is no linkNode, try simple XLink.
   326     let href, baseURI;
   327     node = event.target;
   328     while (node && !href) {
   329       if (node.nodeType == content.Node.ELEMENT_NODE) {
   330         href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
   331         if (href)
   332           baseURI = node.ownerDocument.baseURIObject;
   333       }
   334       node = node.parentNode;
   335     }
   337     // In case of XLink, we don't return the node we got href from since
   338     // callers expect <a>-like elements.
   339     // Note: makeURI() will throw if aUri is not a valid URI.
   340     return [href ? makeURI(href, null, baseURI).spec : null, null];
   341   }
   342 };
   343 ClickEventHandler.init();
   345 ContentLinkHandler.init(this);
   347 addEventListener("DOMWebNotificationClicked", function(event) {
   348   sendAsyncMessage("DOMWebNotificationClicked", {});
   349 }, false);
   351 let PageStyleHandler = {
   352   init: function() {
   353     addMessageListener("PageStyle:Switch", this);
   354     addMessageListener("PageStyle:Disable", this);
   356     // Send a CPOW to the parent so that it can synchronously request
   357     // the list of style sheets.
   358     sendSyncMessage("PageStyle:SetSyncHandler", {}, {syncHandler: this});
   359   },
   361   get markupDocumentViewer() {
   362     return docShell.contentViewer.QueryInterface(Ci.nsIMarkupDocumentViewer);
   363   },
   365   // Called synchronously via CPOW from the parent.
   366   getStyleSheetInfo: function() {
   367     let styleSheets = this._filterStyleSheets(this.getAllStyleSheets());
   368     return {
   369       styleSheets: styleSheets,
   370       authorStyleDisabled: this.markupDocumentViewer.authorStyleDisabled,
   371       preferredStyleSheetSet: content.document.preferredStyleSheetSet
   372     };
   373   },
   375   // Called synchronously via CPOW from the parent.
   376   getAllStyleSheets: function(frameset = content) {
   377     let selfSheets = Array.slice(frameset.document.styleSheets);
   378     let subSheets = Array.map(frameset.frames, frame => this.getAllStyleSheets(frame));
   379     return selfSheets.concat(...subSheets);
   380   },
   382   receiveMessage: function(msg) {
   383     switch (msg.name) {
   384       case "PageStyle:Switch":
   385         this.markupDocumentViewer.authorStyleDisabled = false;
   386         this._stylesheetSwitchAll(content, msg.data.title);
   387         break;
   389       case "PageStyle:Disable":
   390         this.markupDocumentViewer.authorStyleDisabled = true;
   391         break;
   392     }
   393   },
   395   _stylesheetSwitchAll: function (frameset, title) {
   396     if (!title || this._stylesheetInFrame(frameset, title)) {
   397       this._stylesheetSwitchFrame(frameset, title);
   398     }
   400     for (let i = 0; i < frameset.frames.length; i++) {
   401       // Recurse into sub-frames.
   402       this._stylesheetSwitchAll(frameset.frames[i], title);
   403     }
   404   },
   406   _stylesheetSwitchFrame: function (frame, title) {
   407     var docStyleSheets = frame.document.styleSheets;
   409     for (let i = 0; i < docStyleSheets.length; ++i) {
   410       let docStyleSheet = docStyleSheets[i];
   411       if (docStyleSheet.title) {
   412         docStyleSheet.disabled = (docStyleSheet.title != title);
   413       } else if (docStyleSheet.disabled) {
   414         docStyleSheet.disabled = false;
   415       }
   416     }
   417   },
   419   _stylesheetInFrame: function (frame, title) {
   420     return Array.some(frame.document.styleSheets, (styleSheet) => styleSheet.title == title);
   421   },
   423   _filterStyleSheets: function(styleSheets) {
   424     let result = [];
   426     for (let currentStyleSheet of styleSheets) {
   427       if (!currentStyleSheet.title)
   428         continue;
   430       // Skip any stylesheets that don't match the screen media type.
   431       if (currentStyleSheet.media.length > 0) {
   432         let mediaQueryList = currentStyleSheet.media.mediaText;
   433         if (!content.matchMedia(mediaQueryList).matches) {
   434           continue;
   435         }
   436       }
   438       result.push({title: currentStyleSheet.title,
   439                    disabled: currentStyleSheet.disabled});
   440     }
   442     return result;
   443   },
   444 };
   445 PageStyleHandler.init();
   447 let TranslationHandler = {
   448   init: function() {
   449     let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
   450                               .getInterface(Ci.nsIWebProgress);
   451     webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
   452   },
   454   /* nsIWebProgressListener implementation */
   455   onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
   456     if (!aWebProgress.isTopLevel ||
   457         !(aStateFlags & Ci.nsIWebProgressListener.STATE_STOP))
   458       return;
   460     let url = aRequest.name;
   461     if (!url.startsWith("http://") && !url.startsWith("https://"))
   462       return;
   464     // Grab a 60k sample of text from the page.
   465     let encoder = Cc["@mozilla.org/layout/documentEncoder;1?type=text/plain"]
   466                     .createInstance(Ci.nsIDocumentEncoder);
   467     encoder.init(content.document, "text/plain", encoder.SkipInvisibleContent);
   468     let string = encoder.encodeToStringWithMaxLength(60 * 1024);
   470     // Language detection isn't reliable on very short strings.
   471     if (string.length < 100)
   472       return;
   474     LanguageDetector.detectLanguage(string).then(result => {
   475       if (result.confident)
   476         sendAsyncMessage("LanguageDetection:Result", result.language);
   477     });
   478   },
   480   // Unused methods.
   481   onProgressChange: function() {},
   482   onLocationChange: function() {},
   483   onStatusChange:   function() {},
   484   onSecurityChange: function() {},
   486   QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
   487                                          Ci.nsISupportsWeakReference])
   488 };
   490 if (Services.prefs.getBoolPref("browser.translation.detectLanguage"))
   491   TranslationHandler.init();

mercurial