LCOV - code coverage report
Current view: top level - toolkit/content - browser-content.js (source / functions) Hit Total Coverage
Test: output.info Lines: 96 466 20.6 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
       5             : 
       6             : /* eslint-env mozilla/frame-script */
       7             : /* eslint no-unused-vars: ["error", {args: "none"}] */
       8             : /* global sendAsyncMessage */
       9             : 
      10           0 : ChromeUtils.import("resource://gre/modules/Services.jsm");
      11           1 : ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
      12             : 
      13           0 : ChromeUtils.defineModuleGetter(this, "AutoScrollController",
      14           0 :   "resource://gre/modules/AutoScrollController.jsm");
      15           0 : ChromeUtils.defineModuleGetter(this, "BrowserUtils",
      16           0 :   "resource://gre/modules/BrowserUtils.jsm");
      17           0 : ChromeUtils.defineModuleGetter(this, "SelectContentHelper",
      18           0 :   "resource://gre/modules/SelectContentHelper.jsm");
      19           0 : ChromeUtils.defineModuleGetter(this, "FindContent",
      20           0 :   "resource://gre/modules/FindContent.jsm");
      21           0 : ChromeUtils.defineModuleGetter(this, "PrintingContent",
      22           1 :   "resource://gre/modules/PrintingContent.jsm");
      23           0 : ChromeUtils.defineModuleGetter(this, "RemoteFinder",
      24           1 :   "resource://gre/modules/RemoteFinder.jsm");
      25             : 
      26           2 : XPCOMUtils.defineLazyProxy(this, "SelectionSourceContent",
      27           0 :   "resource://gre/modules/SelectionSourceContent.jsm");
      28             : 
      29           0 : XPCOMUtils.defineLazyProxy(this, "DateTimePickerContent", () => {
      30           0 :   let tmp = {};
      31           0 :   ChromeUtils.import("resource://gre/modules/DateTimePickerContent.jsm", tmp);
      32           0 :   return new tmp.DateTimePickerContent(this);
      33           0 : });
      34             : 
      35           0 : var global = this;
      36             : 
      37             : 
      38             : // Lazily load the finder code
      39           0 : addMessageListener("Finder:Initialize", function() {
      40           0 :   let {RemoteFinderListener} = ChromeUtils.import("resource://gre/modules/RemoteFinder.jsm", {});
      41           0 :   new RemoteFinderListener(global);
      42             : });
      43             : 
      44           1 : var AutoScrollListener = {
      45           0 :   handleEvent(event) {
      46           0 :     if (event.isTrusted &
      47           0 :         !event.defaultPrevented &&
      48           0 :         event.button == 1) {
      49           0 :       if (!this._controller) {
      50           0 :         this._controller = new AutoScrollController(global);
      51             :       }
      52           0 :       this._controller.handleEvent(event);
      53             :     }
      54             :   }
      55             : };
      56           0 : Services.els.addSystemEventListener(global, "mousedown", AutoScrollListener, true);
      57             : 
      58           1 : addEventListener("MozOpenDateTimePicker", DateTimePickerContent);
      59             : 
      60           0 : var PopupBlocking = {
      61           0 :   popupData: null,
      62           0 :   popupDataInternal: null,
      63             : 
      64           1 :   init() {
      65           0 :     addEventListener("DOMPopupBlocked", this, true);
      66           0 :     addEventListener("pageshow", this, true);
      67           0 :     addEventListener("pagehide", this, true);
      68             : 
      69           1 :     addMessageListener("PopupBlocking:UnblockPopup", this);
      70           0 :     addMessageListener("PopupBlocking:GetBlockedPopupList", this);
      71             :   },
      72             : 
      73           0 :   receiveMessage(msg) {
      74           0 :     switch (msg.name) {
      75           0 :       case "PopupBlocking:UnblockPopup": {
      76           0 :         let i = msg.data.index;
      77           0 :         if (this.popupData && this.popupData[i]) {
      78           0 :           let data = this.popupData[i];
      79           0 :           let internals = this.popupDataInternal[i];
      80           0 :           let dwi = internals.requestingWindow;
      81             : 
      82             :           // If we have a requesting window and the requesting document is
      83             :           // still the current document, open the popup.
      84           0 :           if (dwi && dwi.document == internals.requestingDocument) {
      85           0 :             dwi.open(data.popupWindowURIspec, data.popupWindowName, data.popupWindowFeatures);
      86             :           }
      87             :         }
      88           0 :         break;
      89             :       }
      90             : 
      91           0 :       case "PopupBlocking:GetBlockedPopupList": {
      92           0 :         let popupData = [];
      93           0 :         let length = this.popupData ? this.popupData.length : 0;
      94             : 
      95             :         // Limit 15 popup URLs to be reported through the UI
      96           0 :         length = Math.min(length, 15);
      97             : 
      98           0 :         for (let i = 0; i < length; i++) {
      99           0 :           let popupWindowURIspec = this.popupData[i].popupWindowURIspec;
     100             : 
     101           0 :           if (popupWindowURIspec == global.content.location.href) {
     102           0 :             popupWindowURIspec = "<self>";
     103             :           } else {
     104             :             // Limit 500 chars to be sent because the URI will be cropped
     105             :             // by the UI anyway, and data: URIs can be significantly larger.
     106           0 :             popupWindowURIspec = popupWindowURIspec.substring(0, 500);
     107             :           }
     108             : 
     109           0 :           popupData.push({popupWindowURIspec});
     110             :         }
     111             : 
     112           0 :         sendAsyncMessage("PopupBlocking:ReplyGetBlockedPopupList", {popupData});
     113           0 :         break;
     114             :       }
     115             :     }
     116             :   },
     117             : 
     118           1 :   handleEvent(ev) {
     119           1 :     switch (ev.type) {
     120             :       case "DOMPopupBlocked":
     121           0 :         return this.onPopupBlocked(ev);
     122             :       case "pageshow":
     123           0 :         return this._removeIrrelevantPopupData();
     124             :       case "pagehide":
     125           1 :         return this._removeIrrelevantPopupData(ev.target);
     126             :     }
     127           0 :     return undefined;
     128             :   },
     129             : 
     130           1 :   onPopupBlocked(ev) {
     131           0 :     if (!this.popupData) {
     132           0 :       this.popupData = [];
     133           0 :       this.popupDataInternal = [];
     134             :     }
     135             : 
     136           0 :     let obj = {
     137           0 :       popupWindowURIspec: ev.popupWindowURI ? ev.popupWindowURI.spec : "about:blank",
     138           0 :       popupWindowFeatures: ev.popupWindowFeatures,
     139           0 :       popupWindowName: ev.popupWindowName
     140             :     };
     141             : 
     142           0 :     let internals = {
     143           0 :       requestingWindow: ev.requestingWindow,
     144           0 :       requestingDocument: ev.requestingWindow.document,
     145             :     };
     146             : 
     147           0 :     this.popupData.push(obj);
     148           0 :     this.popupDataInternal.push(internals);
     149           0 :     this.updateBlockedPopups(true);
     150             :   },
     151             : 
     152           1 :   _removeIrrelevantPopupData(removedDoc = null) {
     153           1 :     if (this.popupData) {
     154           0 :       let i = 0;
     155           0 :       let oldLength = this.popupData.length;
     156           0 :       while (i < this.popupData.length) {
     157           0 :         let {requestingWindow, requestingDocument} = this.popupDataInternal[i];
     158             :         // Filter out irrelevant reports.
     159           0 :         if (requestingWindow && requestingWindow.document == requestingDocument &&
     160           0 :             requestingDocument != removedDoc) {
     161           0 :           i++;
     162             :         } else {
     163           0 :           this.popupData.splice(i, 1);
     164           0 :           this.popupDataInternal.splice(i, 1);
     165             :         }
     166             :       }
     167           0 :       if (this.popupData.length == 0) {
     168           0 :         this.popupData = null;
     169           0 :         this.popupDataInternal = null;
     170             :       }
     171           0 :       if (!this.popupData || oldLength > this.popupData.length) {
     172           0 :         this.updateBlockedPopups(false);
     173             :       }
     174             :     }
     175             :   },
     176             : 
     177           1 :   updateBlockedPopups(freshPopup) {
     178           0 :     sendAsyncMessage("PopupBlocking:UpdateBlockedPopups",
     179           0 :       {
     180           0 :         count: this.popupData ? this.popupData.length : 0,
     181           0 :         freshPopup
     182             :       });
     183             :   },
     184             : };
     185           1 : PopupBlocking.init();
     186             : 
     187           1 : var Printing = {
     188           0 :   MESSAGES: [
     189             :     "Printing:Preview:Enter",
     190             :     "Printing:Preview:Exit",
     191             :     "Printing:Preview:Navigate",
     192             :     "Printing:Preview:ParseDocument",
     193             :     "Printing:Print",
     194             :   ],
     195             : 
     196           0 :   init() {
     197           6 :     this.MESSAGES.forEach(msgName => addMessageListener(msgName, this));
     198           0 :     addEventListener("PrintingError", this, true);
     199           0 :     addEventListener("printPreviewUpdate", this, true);
     200             :   },
     201             : 
     202           1 :   handleEvent(event) {
     203           0 :     return PrintingContent.handleEvent(global, event);
     204             :   },
     205             : 
     206           0 :   receiveMessage(message) {
     207           0 :     return PrintingContent.receiveMessage(global, message);
     208             :   },
     209             : };
     210           0 : Printing.init();
     211             : 
     212             : function SwitchDocumentDirection(aWindow) {
     213             :  // document.dir can also be "auto", in which case it won't change
     214           0 :   if (aWindow.document.dir == "ltr" || aWindow.document.dir == "") {
     215           0 :     aWindow.document.dir = "rtl";
     216           0 :   } else if (aWindow.document.dir == "rtl") {
     217           0 :     aWindow.document.dir = "ltr";
     218             :   }
     219           0 :   for (let run = 0; run < aWindow.frames.length; run++) {
     220           0 :     SwitchDocumentDirection(aWindow.frames[run]);
     221             :   }
     222             : }
     223             : 
     224           0 : addMessageListener("SwitchDocumentDirection", () => {
     225           0 :   SwitchDocumentDirection(content.window);
     226             : });
     227             : 
     228           0 : var FindBar = {
     229             :   /* Please keep in sync with toolkit/content/widgets/findbar.xml */
     230           1 :   FIND_NORMAL: 0,
     231           1 :   FIND_TYPEAHEAD: 1,
     232           1 :   FIND_LINKS: 2,
     233             : 
     234           1 :   _findMode: 0,
     235             : 
     236             :   /**
     237             :    * _findKey and _findModifiers are used to determine whether a keypress
     238             :    * is a user attempting to use the find shortcut, after which we'll
     239             :    * route keypresses to the parent until we know the findbar has focus
     240             :    * there. To do this, we need shortcut data from the parent.
     241             :    */
     242           1 :   _findKey: null,
     243           1 :   _findModifiers: null,
     244             : 
     245           0 :   init() {
     246           0 :     addMessageListener("Findbar:UpdateState", this);
     247           0 :     Services.els.addSystemEventListener(global, "keypress", this, false);
     248           0 :     Services.els.addSystemEventListener(global, "mouseup", this, false);
     249           1 :     this._initShortcutData();
     250             :   },
     251             : 
     252           0 :   receiveMessage(msg) {
     253           0 :     switch (msg.name) {
     254             :       case "Findbar:UpdateState":
     255           0 :         this._findMode = msg.data.findMode;
     256           0 :         this._quickFindTimeout = msg.data.hasQuickFindTimeout;
     257           0 :         if (msg.data.isOpenAndFocused) {
     258           0 :           this._keepPassingUntilToldOtherwise = false;
     259             :         }
     260             :         break;
     261             :       case "Findbar:ShortcutData":
     262             :         // Set us up to never need this again for the lifetime of this process,
     263             :         // and remove the listener.
     264           1 :         Services.cpmm.initialProcessData.findBarShortcutData = msg.data;
     265           1 :         Services.cpmm.removeMessageListener("Findbar:ShortcutData", this);
     266           1 :         this._initShortcutData(msg.data);
     267             :         break;
     268             :     }
     269             :   },
     270             : 
     271           1 :   handleEvent(event) {
     272           0 :     switch (event.type) {
     273             :       case "keypress":
     274           0 :         this._onKeypress(event);
     275             :         break;
     276             :       case "mouseup":
     277           0 :         this._onMouseup(event);
     278             :         break;
     279             :     }
     280             :   },
     281             : 
     282             :   /**
     283             :    * Use initial process data for find key/modifier data if we have it.
     284             :    * Otherwise, add a listener so we get the data when the parent process has
     285             :    * it.
     286             :    */
     287           1 :   _initShortcutData(data = Services.cpmm.initialProcessData.findBarShortcutData) {
     288           2 :     if (data) {
     289           1 :       this._findKey = data.key;
     290           1 :       this._findModifiers = data.modifiers;
     291             :     } else {
     292           1 :       Services.cpmm.addMessageListener("Findbar:ShortcutData", this);
     293             :     }
     294             :   },
     295             : 
     296             :   /**
     297             :    * Check whether this key event will start the findbar in the parent,
     298             :    * in which case we should pass any further key events to the parent to avoid
     299             :    * them being lost.
     300             :    * @param aEvent the key event to check.
     301             :    */
     302           1 :   _eventMatchesFindShortcut(aEvent) {
     303           0 :     let modifiers = this._findModifiers;
     304           0 :     if (!modifiers) {
     305           0 :       return false;
     306             :     }
     307           0 :     return aEvent.ctrlKey == modifiers.ctrlKey && aEvent.altKey == modifiers.altKey &&
     308           0 :       aEvent.shiftKey == modifiers.shiftKey && aEvent.metaKey == modifiers.metaKey &&
     309           0 :       aEvent.key == this._findKey;
     310           0 :   },
     311             : 
     312             :   /**
     313             :    * Returns whether FAYT can be used for the given event in
     314             :    * the current content state.
     315             :    */
     316           0 :   _canAndShouldFastFind() {
     317           0 :     let should = false;
     318           0 :     let can = BrowserUtils.canFastFind(content);
     319           0 :     if (can) {
     320             :       // XXXgijs: why all these shenanigans? Why not use the event's target?
     321           0 :       let focusedWindow = {};
     322           0 :       let elt = Services.focus.getFocusedElementForWindow(content, true, focusedWindow);
     323           0 :       let win = focusedWindow.value;
     324           0 :       should = BrowserUtils.shouldFastFind(elt, win);
     325             :     }
     326           0 :     return { can, should };
     327           0 :   },
     328             : 
     329           1 :   _onKeypress(event) {
     330           0 :     const FAYT_LINKS_KEY = "'";
     331           0 :     const FAYT_TEXT_KEY = "/";
     332           0 :     if (this._eventMatchesFindShortcut(event)) {
     333           0 :       this._keepPassingUntilToldOtherwise = true;
     334             :     }
     335             :     // Useless keys:
     336           0 :     if (event.ctrlKey || event.altKey || event.metaKey || event.defaultPrevented) {
     337           0 :       return;
     338             :     }
     339             : 
     340             :     // Check the focused element etc.
     341           0 :     let fastFind = this._canAndShouldFastFind();
     342             : 
     343             :     // Can we even use find in this page at all?
     344           0 :     if (!fastFind.can) {
     345           0 :       return;
     346             :     }
     347           0 :     if (this._keepPassingUntilToldOtherwise) {
     348           0 :       this._passKeyToParent(event);
     349           0 :       return;
     350             :     }
     351           0 :     if (!fastFind.should) {
     352           0 :       return;
     353             :     }
     354             : 
     355           0 :     let charCode = event.charCode;
     356             :     // If the find bar is open and quick find is on, send the key to the parent.
     357           0 :     if (this._findMode != this.FIND_NORMAL && this._quickFindTimeout) {
     358           0 :       if (!charCode)
     359           0 :         return;
     360           0 :       this._passKeyToParent(event);
     361           0 :     } else {
     362           0 :       let key = charCode ? String.fromCharCode(charCode) : null;
     363           0 :       let manualstartFAYT = (key == FAYT_LINKS_KEY || key == FAYT_TEXT_KEY);
     364           0 :       let autostartFAYT = !manualstartFAYT && RemoteFinder._findAsYouType && key && key != " ";
     365           0 :       if (manualstartFAYT || autostartFAYT) {
     366           0 :         let mode = (key == FAYT_LINKS_KEY || (autostartFAYT && RemoteFinder._typeAheadLinksOnly)) ?
     367           0 :           this.FIND_LINKS : this.FIND_TYPEAHEAD;
     368             :         // Set _findMode immediately (without waiting for child->parent->child roundtrip)
     369             :         // to ensure we pass any further keypresses, too.
     370           0 :         this._findMode = mode;
     371           0 :         this._passKeyToParent(event);
     372             :       }
     373             :     }
     374           0 :   },
     375             : 
     376           0 :   _passKeyToParent(event) {
     377           0 :     event.preventDefault();
     378             :     // These are the properties required to dispatch another 'real' event
     379             :     // to the findbar in the parent in _dispatchKeypressEvent in findbar.xml .
     380             :     // If you make changes here, verify that that method can still do its job.
     381           0 :     const kRequiredProps = [
     382             :       "type", "bubbles", "cancelable", "ctrlKey", "altKey", "shiftKey",
     383             :       "metaKey", "keyCode", "charCode",
     384             :     ];
     385           0 :     let fakeEvent = {};
     386           0 :     for (let prop of kRequiredProps) {
     387           0 :       fakeEvent[prop] = event[prop];
     388             :     }
     389           0 :     sendAsyncMessage("Findbar:Keypress", fakeEvent);
     390             :   },
     391             : 
     392           0 :   _onMouseup(event) {
     393           0 :     if (this._findMode != this.FIND_NORMAL)
     394           0 :       sendAsyncMessage("Findbar:Mouseup");
     395             :   },
     396             : };
     397           1 : FindBar.init();
     398             : 
     399           0 : let WebChannelMessageToChromeListener = {
     400             :   // Preference containing the list (space separated) of origins that are
     401             :   // allowed to send non-string values through a WebChannel, mainly for
     402             :   // backwards compatability. See bug 1238128 for more information.
     403           1 :   URL_WHITELIST_PREF: "webchannel.allowObject.urlWhitelist",
     404             : 
     405             :   // Cached list of whitelisted principals, we avoid constructing this if the
     406             :   // value in `_lastWhitelistValue` hasn't changed since we constructed it last.
     407           1 :   _cachedWhitelist: [],
     408           1 :   _lastWhitelistValue: "",
     409             : 
     410           1 :   init() {
     411           2 :     addEventListener("WebChannelMessageToChrome", e => {
     412           0 :       this._onMessageToChrome(e);
     413           0 :     }, true, true);
     414             :   },
     415             : 
     416           1 :   _getWhitelistedPrincipals() {
     417           0 :     let whitelist = Services.prefs.getCharPref(this.URL_WHITELIST_PREF);
     418           0 :     if (whitelist != this._lastWhitelistValue) {
     419           0 :       let urls = whitelist.split(/\s+/);
     420           0 :       this._cachedWhitelist = urls.map(origin =>
     421           0 :         Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin));
     422             :     }
     423           0 :     return this._cachedWhitelist;
     424           0 :   },
     425             : 
     426           1 :   _onMessageToChrome(e) {
     427             :     // If target is window then we want the document principal, otherwise fallback to target itself.
     428           0 :     let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal;
     429             : 
     430           0 :     if (e.detail) {
     431           0 :       if (typeof e.detail != "string") {
     432             :         // Check if the principal is one of the ones that's allowed to send
     433             :         // non-string values for e.detail.  They're whitelisted by site origin,
     434             :         // so we compare on originNoSuffix in order to avoid other origin attributes
     435             :         // that are not relevant here, such as containers or private browsing.
     436           0 :         let objectsAllowed = this._getWhitelistedPrincipals().some(whitelisted =>
     437           0 :           principal.originNoSuffix == whitelisted.originNoSuffix);
     438           0 :         if (!objectsAllowed) {
     439           0 :           Cu.reportError("WebChannelMessageToChrome sent with an object from a non-whitelisted principal");
     440           0 :           return;
     441             :         }
     442             :       }
     443           0 :       sendAsyncMessage("WebChannelMessageToChrome", e.detail, { eventTarget: e.target }, principal);
     444             :     } else {
     445           0 :       Cu.reportError("WebChannel message failed. No message detail.");
     446             :     }
     447           0 :   }
     448             : };
     449             : 
     450           1 : WebChannelMessageToChromeListener.init();
     451             : 
     452             : // This should be kept in sync with /browser/base/content.js.
     453             : // Add message listener for "WebChannelMessageToContent" messages from chrome scripts.
     454           0 : addMessageListener("WebChannelMessageToContent", function(e) {
     455           0 :   if (e.data) {
     456             :     // e.objects.eventTarget will be defined if sending a response to
     457             :     // a WebChannelMessageToChrome event. An unsolicited send
     458             :     // may not have an eventTarget defined, in this case send to the
     459             :     // main content window.
     460           0 :     let eventTarget = e.objects.eventTarget || content;
     461             : 
     462             :     // Use nodePrincipal if available, otherwise fallback to document principal.
     463           0 :     let targetPrincipal = eventTarget instanceof Ci.nsIDOMWindow ? eventTarget.document.nodePrincipal : eventTarget.nodePrincipal;
     464             : 
     465           0 :     if (e.principal.subsumes(targetPrincipal)) {
     466             :       // If eventTarget is a window, use it as the targetWindow, otherwise
     467             :       // find the window that owns the eventTarget.
     468           0 :       let targetWindow = eventTarget instanceof Ci.nsIDOMWindow ? eventTarget : eventTarget.ownerGlobal;
     469             : 
     470           0 :       eventTarget.dispatchEvent(new targetWindow.CustomEvent("WebChannelMessageToContent", {
     471           0 :         detail: Cu.cloneInto({
     472           0 :           id: e.data.id,
     473           0 :           message: e.data.message,
     474           0 :         }, targetWindow),
     475             :       }));
     476             :     } else {
     477           0 :       Cu.reportError("WebChannel message failed. Principal mismatch.");
     478             :     }
     479             :   } else {
     480           0 :     Cu.reportError("WebChannel message failed. No message data.");
     481             :   }
     482             : });
     483             : 
     484           1 : var AudioPlaybackListener = {
     485           0 :   QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
     486             : 
     487           0 :   init() {
     488           1 :     Services.obs.addObserver(this, "audio-playback");
     489             : 
     490           0 :     addMessageListener("AudioPlayback", this);
     491           0 :     addEventListener("unload", () => {
     492           1 :       AudioPlaybackListener.uninit();
     493             :     });
     494             :   },
     495             : 
     496           1 :   uninit() {
     497           1 :     Services.obs.removeObserver(this, "audio-playback");
     498             : 
     499           1 :     removeMessageListener("AudioPlayback", this);
     500             :   },
     501             : 
     502           1 :   handleMediaControlMessage(msg) {
     503           0 :     let utils = global.content.QueryInterface(Ci.nsIInterfaceRequestor)
     504           0 :                               .getInterface(Ci.nsIDOMWindowUtils);
     505           0 :     let suspendTypes = Ci.nsISuspendedTypes;
     506           0 :     switch (msg) {
     507             :       case "mute":
     508           0 :         utils.audioMuted = true;
     509             :         break;
     510             :       case "unmute":
     511           0 :         utils.audioMuted = false;
     512             :         break;
     513             :       case "lostAudioFocus":
     514           0 :         utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE_DISPOSABLE;
     515             :         break;
     516             :       case "lostAudioFocusTransiently":
     517           0 :         utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE;
     518             :         break;
     519             :       case "gainAudioFocus":
     520           0 :         utils.mediaSuspend = suspendTypes.NONE_SUSPENDED;
     521             :         break;
     522             :       case "mediaControlPaused":
     523           0 :         utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE_DISPOSABLE;
     524             :         break;
     525             :       case "mediaControlStopped":
     526           0 :         utils.mediaSuspend = suspendTypes.SUSPENDED_STOP_DISPOSABLE;
     527             :         break;
     528             :       case "resumeMedia":
     529           0 :         utils.mediaSuspend = suspendTypes.NONE_SUSPENDED;
     530             :         break;
     531             :       default:
     532           0 :         dump("Error : wrong media control msg!\n");
     533             :         break;
     534             :     }
     535             :   },
     536             : 
     537           1 :   observe(subject, topic, data) {
     538           0 :     if (topic === "audio-playback") {
     539           0 :       if (subject && subject.top == global.content) {
     540           0 :         let name = "AudioPlayback:";
     541           0 :         if (data === "activeMediaBlockStart") {
     542           0 :           name += "ActiveMediaBlockStart";
     543           0 :         } else if (data === "activeMediaBlockStop") {
     544           0 :           name += "ActiveMediaBlockStop";
     545             :         } else {
     546           0 :           name += (data === "active") ? "Start" : "Stop";
     547             :         }
     548           0 :         sendAsyncMessage(name);
     549             :       }
     550             :     }
     551             :   },
     552             : 
     553           1 :   receiveMessage(msg) {
     554           0 :     if (msg.name == "AudioPlayback") {
     555           0 :       this.handleMediaControlMessage(msg.data.type);
     556             :     }
     557             :   },
     558             : };
     559           1 : AudioPlaybackListener.init();
     560             : 
     561           1 : var UnselectedTabHoverObserver = {
     562           0 :   init() {
     563           0 :     addMessageListener("Browser:UnselectedTabHover", this);
     564           1 :     addEventListener("UnselectedTabHover:Enable", this);
     565           1 :     addEventListener("UnselectedTabHover:Disable", this);
     566             :   },
     567           1 :   receiveMessage(message) {
     568           0 :     Services.obs.notifyObservers(content.window, "unselected-tab-hover",
     569           0 :                                  message.data.hovered);
     570             :   },
     571           1 :   handleEvent(event) {
     572           0 :     sendAsyncMessage("UnselectedTabHover:Toggle",
     573           0 :                      { enable: event.type == "UnselectedTabHover:Enable" });
     574             :   }
     575             : };
     576           1 : UnselectedTabHoverObserver.init();
     577             : 
     578           1 : addMessageListener("Browser:PurgeSessionHistory", function BrowserPurgeHistory() {
     579           0 :   let sessionHistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
     580           0 :   if (!sessionHistory) {
     581           0 :     return;
     582             :   }
     583             : 
     584             :   // place the entry at current index at the end of the history list, so it won't get removed
     585           0 :   if (sessionHistory.index < sessionHistory.count - 1) {
     586           0 :     let legacy = sessionHistory.legacySHistory;
     587           0 :     legacy.QueryInterface(Ci.nsISHistoryInternal);
     588           0 :     let indexEntry = legacy.getEntryAtIndex(sessionHistory.index, false);
     589           0 :     indexEntry.QueryInterface(Ci.nsISHEntry);
     590           0 :     legacy.addEntry(indexEntry, true);
     591             :   }
     592             : 
     593           0 :   let purge = sessionHistory.count;
     594           0 :   if (global.content.location.href != "about:blank") {
     595           0 :     --purge; // Don't remove the page the user's staring at from shistory
     596             :   }
     597             : 
     598           0 :   if (purge > 0) {
     599           0 :     sessionHistory.legacySHistory.PurgeHistory(purge);
     600             :   }
     601           0 : });
     602             : 
     603           0 : addMessageListener("ViewSource:GetSelection", SelectionSourceContent);
     604             : 
     605           0 : addEventListener("MozApplicationManifest", function(e) {
     606           0 :   let doc = e.target;
     607           0 :   let info = {
     608           0 :     uri: doc.documentURI,
     609           0 :     characterSet: doc.characterSet,
     610           0 :     manifest: doc.documentElement.getAttribute("manifest"),
     611           0 :     principal: doc.nodePrincipal,
     612             :   };
     613           0 :   sendAsyncMessage("MozApplicationManifest", info);
     614           0 : }, false);
     615             : 
     616           1 : let AutoCompletePopup = {
     617           1 :   QueryInterface: ChromeUtils.generateQI([Ci.nsIAutoCompletePopup]),
     618             : 
     619           1 :   _connected: false,
     620             : 
     621           1 :   MESSAGES: [
     622           0 :     "FormAutoComplete:HandleEnter",
     623           1 :     "FormAutoComplete:PopupClosed",
     624           1 :     "FormAutoComplete:PopupOpened",
     625           0 :     "FormAutoComplete:RequestFocus",
     626             :   ],
     627             : 
     628           1 :   init() {
     629           1 :     addEventListener("unload", this);
     630           0 :     addEventListener("DOMContentLoaded", this);
     631             :     // WebExtension browserAction is preloaded and does not receive DCL, wait
     632             :     // on pageshow so we can hookup the formfill controller.
     633           0 :     addEventListener("pageshow", this, true);
     634             : 
     635           0 :     for (let messageName of this.MESSAGES) {
     636           4 :       addMessageListener(messageName, this);
     637             :     }
     638             : 
     639           0 :     this._input = null;
     640           0 :     this._popupOpen = false;
     641             :   },
     642             : 
     643           0 :   destroy() {
     644           1 :     if (this._connected) {
     645           0 :       let controller = Cc["@mozilla.org/satchel/form-fill-controller;1"]
     646           0 :                          .getService(Ci.nsIFormFillController);
     647           0 :       controller.detachFromBrowser(docShell);
     648           0 :       this._connected = false;
     649             :     }
     650             : 
     651           1 :     removeEventListener("pageshow", this);
     652           1 :     removeEventListener("unload", this);
     653           1 :     removeEventListener("DOMContentLoaded", this);
     654             : 
     655           0 :     for (let messageName of this.MESSAGES) {
     656           0 :       removeMessageListener(messageName, this);
     657             :     }
     658             :   },
     659             : 
     660           1 :   connect() {
     661           0 :     if (this._connected) {
     662           0 :       return;
     663             :     }
     664             :     // We need to wait for a content viewer to be available
     665             :     // before we can attach our AutoCompletePopup handler,
     666             :     // since nsFormFillController assumes one will exist
     667             :     // when we call attachToBrowser.
     668             : 
     669             :     // Hook up the form fill autocomplete controller.
     670           0 :     let controller = Cc["@mozilla.org/satchel/form-fill-controller;1"]
     671           0 :                        .getService(Ci.nsIFormFillController);
     672           0 :     controller.attachToBrowser(docShell,
     673           0 :                                this.QueryInterface(Ci.nsIAutoCompletePopup));
     674           0 :     this._connected = true;
     675           0 :   },
     676             : 
     677           0 :   handleEvent(event) {
     678           0 :     switch (event.type) {
     679             :       case "pageshow": {
     680           0 :         removeEventListener("pageshow", this);
     681           0 :         this.connect();
     682             :         break;
     683             :       }
     684             : 
     685             :       case "DOMContentLoaded": {
     686           0 :         removeEventListener("DOMContentLoaded", this);
     687           0 :         this.connect();
     688             :         break;
     689             :       }
     690             : 
     691             :       case "unload": {
     692           1 :         this.destroy();
     693             :         break;
     694             :       }
     695             :     }
     696             :   },
     697             : 
     698           1 :   receiveMessage(message) {
     699           0 :     switch (message.name) {
     700           0 :       case "FormAutoComplete:HandleEnter": {
     701           0 :         this.selectedIndex = message.data.selectedIndex;
     702             : 
     703           0 :         let controller = Cc["@mozilla.org/autocomplete/controller;1"]
     704           0 :                            .getService(Ci.nsIAutoCompleteController);
     705           0 :         controller.handleEnter(message.data.isPopupSelection);
     706           0 :         break;
     707             :       }
     708             : 
     709             :       case "FormAutoComplete:PopupClosed": {
     710           0 :         this._popupOpen = false;
     711             :         break;
     712             :       }
     713             : 
     714             :       case "FormAutoComplete:PopupOpened": {
     715           0 :         this._popupOpen = true;
     716             :         break;
     717             :       }
     718             : 
     719             :       case "FormAutoComplete:RequestFocus": {
     720           0 :         if (this._input) {
     721           0 :           this._input.focus();
     722             :         }
     723             :         break;
     724             :       }
     725             :     }
     726             :   },
     727             : 
     728           1 :   get input() { return this._input; },
     729           1 :   get overrideValue() { return null; },
     730           0 :   set selectedIndex(index) {
     731           0 :     sendAsyncMessage("FormAutoComplete:SetSelectedIndex", { index });
     732             :   },
     733           1 :   get selectedIndex() {
     734             :     // selectedIndex getter must be synchronous because we need the
     735             :     // correct value when the controller is in controller::HandleEnter.
     736             :     // We can't easily just let the parent inform us the new value every
     737             :     // time it changes because not every action that can change the
     738             :     // selectedIndex is trivial to catch (e.g. moving the mouse over the
     739             :     // list).
     740           0 :     return sendSyncMessage("FormAutoComplete:GetSelectedIndex", {});
     741             :   },
     742           1 :   get popupOpen() {
     743           0 :     return this._popupOpen;
     744             :   },
     745             : 
     746           0 :   openAutocompletePopup(input, element) {
     747           0 :     if (this._popupOpen || !input) {
     748           0 :       return;
     749             :     }
     750             : 
     751           0 :     let rect = BrowserUtils.getElementBoundingScreenRect(element);
     752           0 :     let window = element.ownerGlobal;
     753           0 :     let dir = window.getComputedStyle(element).direction;
     754           0 :     let results = this.getResultsFromController(input);
     755             : 
     756           0 :     sendAsyncMessage("FormAutoComplete:MaybeOpenPopup",
     757           0 :                      { results, rect, dir });
     758           0 :     this._input = input;
     759           0 :   },
     760             : 
     761           1 :   closePopup() {
     762             :     // We set this here instead of just waiting for the
     763             :     // PopupClosed message to do it so that we don't end
     764             :     // up in a state where the content thinks that a popup
     765             :     // is open when it isn't (or soon won't be).
     766           0 :     this._popupOpen = false;
     767           0 :     sendAsyncMessage("FormAutoComplete:ClosePopup", {});
     768             :   },
     769             : 
     770           0 :   invalidate() {
     771           0 :     if (this._popupOpen) {
     772           0 :       let results = this.getResultsFromController(this._input);
     773           0 :       sendAsyncMessage("FormAutoComplete:Invalidate", { results });
     774             :     }
     775             :   },
     776             : 
     777           0 :   selectBy(reverse, page) {
     778           0 :     this._index = sendSyncMessage("FormAutoComplete:SelectBy", {
     779           0 :       reverse,
     780           0 :       page
     781             :     });
     782             :   },
     783             : 
     784           1 :   getResultsFromController(inputField) {
     785           0 :     let results = [];
     786             : 
     787           0 :     if (!inputField) {
     788           0 :       return results;
     789             :     }
     790             : 
     791           0 :     let controller = inputField.controller;
     792           0 :     if (!(controller instanceof Ci.nsIAutoCompleteController)) {
     793           0 :       return results;
     794             :     }
     795             : 
     796           0 :     for (let i = 0; i < controller.matchCount; ++i) {
     797           0 :       let result = {};
     798           0 :       result.value = controller.getValueAt(i);
     799           0 :       result.label = controller.getLabelAt(i);
     800           0 :       result.comment = controller.getCommentAt(i);
     801           0 :       result.style = controller.getStyleAt(i);
     802           0 :       result.image = controller.getImageAt(i);
     803           0 :       results.push(result);
     804             :     }
     805             : 
     806           0 :     return results;
     807           0 :   },
     808             : };
     809             : 
     810           1 : AutoCompletePopup.init();
     811             : 
     812           1 : addEventListener("mozshowdropdown", event => {
     813           0 :   if (!event.isTrusted)
     814           0 :     return;
     815             : 
     816           0 :   if (!SelectContentHelper.open) {
     817           0 :     new SelectContentHelper(event.target, {isOpenedViaTouch: false}, this);
     818             :   }
     819             : });
     820             : 
     821           1 : addEventListener("mozshowdropdown-sourcetouch", event => {
     822           0 :   if (!event.isTrusted)
     823           0 :     return;
     824             : 
     825           0 :   if (!SelectContentHelper.open) {
     826           0 :     new SelectContentHelper(event.target, {isOpenedViaTouch: true}, this);
     827             :   }
     828             : });
     829             : 
     830           1 : let ExtFind = {
     831           1 :   init() {
     832           1 :     addMessageListener("ext-Finder:CollectResults", this);
     833           1 :     addMessageListener("ext-Finder:HighlightResults", this);
     834           1 :     addMessageListener("ext-Finder:clearHighlighting", this);
     835             :   },
     836             : 
     837           1 :   _findContent: null,
     838             : 
     839           1 :   async receiveMessage(message) {
     840           0 :     if (!this._findContent) {
     841           0 :       this._findContent = new FindContent(docShell);
     842             :     }
     843             : 
     844           0 :     let data;
     845           0 :     switch (message.name) {
     846             :       case "ext-Finder:CollectResults":
     847           0 :         this.finderInited = true;
     848           0 :         data = await this._findContent.findRanges(message.data);
     849           0 :         sendAsyncMessage("ext-Finder:CollectResultsFinished", data);
     850             :         break;
     851             :       case "ext-Finder:HighlightResults":
     852           0 :         data = this._findContent.highlightResults(message.data);
     853           0 :         sendAsyncMessage("ext-Finder:HighlightResultsFinished", data);
     854             :         break;
     855             :       case "ext-Finder:clearHighlighting":
     856           0 :         this._findContent.highlighter.highlight(false);
     857             :         break;
     858             :     }
     859             :   },
     860             : };
     861             : 
     862           1 : ExtFind.init();

Generated by: LCOV version 1.13-14-ga5dd952