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.

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

mercurial