Bug 1529969 - Only request info about the current engine to the parent; r=adw,Mardak
authorJim Porter <jporter@mozilla.com>
Tue, 17 Dec 2019 21:02:52 +0000
changeset 507470 1fa1be74b9b0e3ab1f84a868737e200b2cf2d543
parent 507469 d71785877032d82727754bc5f1c7e0d154359fbe
child 507471 9607b6cf43ddcbd86999069cb34259f28e7d2c84
push id103461
push userjporter@mozilla.com
push dateTue, 17 Dec 2019 21:04:08 +0000
treeherderautoland@1fa1be74b9b0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersadw, Mardak
bugs1529969
milestone73.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1529969 - Only request info about the current engine to the parent; r=adw,Mardak This patch adds a new ContentSearchHandoffUI class to be used when search handoff is enabled in the newtab page, avoiding the much more complex ContentSearchUI. This also reduces the amount of unnecessary information being sent across processes. Differential Revision: https://phabricator.services.mozilla.com/D48776
browser/base/content/contentSearchHandoffUI.js
browser/base/jar.mn
browser/components/newtab/bin/render-activity-stream-html.js
browser/components/newtab/content-src/components/Search/Search.jsx
browser/components/newtab/data/content/activity-stream.bundle.js
browser/components/newtab/prerendered/activity-stream-debug.html
browser/components/newtab/prerendered/activity-stream.html
browser/components/newtab/test/unit/unit-entry.js
browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
browser/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml
browser/modules/ContentSearch.jsm
browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css
new file mode 100644
--- /dev/null
+++ b/browser/base/content/contentSearchHandoffUI.js
@@ -0,0 +1,79 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+function ContentSearchHandoffUIController() {
+  this._isPrivateWindow = false;
+  this._engineIcon = null;
+
+  window.addEventListener("ContentSearchService", this);
+  this._sendMsg("GetEngine");
+}
+
+ContentSearchHandoffUIController.prototype = {
+  handleEvent(event) {
+    let methodName = "_onMsg" + event.detail.type;
+    if (methodName in this) {
+      this[methodName](event.detail.data);
+    }
+  },
+
+  _onMsgEngine({ isPrivateWindow, engine }) {
+    this._isPrivateWindow = isPrivateWindow;
+    this._updateEngineIcon(engine);
+  },
+
+  _onMsgCurrentEngine(engine) {
+    if (!this._isPrivateWindow) {
+      this._updateEngineIcon(engine);
+    }
+  },
+
+  _onMsgCurrentPrivateEngine(engine) {
+    if (this._isPrivateWindow) {
+      this._updateEngineIcon(engine);
+    }
+  },
+
+  _updateEngineIcon(engine) {
+    if (this._engineIcon) {
+      URL.revokeObjectURL(this._engineIcon);
+    }
+
+    if (engine.iconData) {
+      this._engineIcon = this._getFaviconURIFromIconData(engine.iconData);
+    } else {
+      this._engineIcon = "chrome://mozapps/skin/places/defaultFavicon.svg";
+    }
+
+    document.body.style.setProperty(
+      "--newtab-search-icon",
+      "url(" + this._engineIcon + ")"
+    );
+  },
+
+  // If the favicon is an array buffer, convert it into a Blob URI.
+  // Otherwise just return the plain URI.
+  _getFaviconURIFromIconData(data) {
+    if (typeof data === "string") {
+      return data;
+    }
+
+    // If typeof(data) != "string", we assume it's an ArrayBuffer
+    let blob = new Blob([data]);
+    return URL.createObjectURL(blob);
+  },
+
+  _sendMsg(type, data = null) {
+    dispatchEvent(
+      new CustomEvent("ContentSearchClient", {
+        detail: {
+          type,
+          data,
+        },
+      })
+    );
+  },
+};
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -89,16 +89,17 @@ browser.jar:
         content/browser/static-robot.png              (content/static-robot.png)
         content/browser/safeMode.css                  (content/safeMode.css)
         content/browser/safeMode.js                   (content/safeMode.js)
         content/browser/safeMode.xhtml                (content/safeMode.xhtml)
         content/browser/sanitize.xhtml                (content/sanitize.xhtml)
         content/browser/sanitizeDialog.js             (content/sanitizeDialog.js)
         content/browser/sanitizeDialog.css            (content/sanitizeDialog.css)
         content/browser/contentSearchUI.js            (content/contentSearchUI.js)
+        content/browser/contentSearchHandoffUI.js     (content/contentSearchHandoffUI.js)
         content/browser/contentSearchUI.css           (content/contentSearchUI.css)
         content/browser/tabbrowser.css                (content/tabbrowser.css)
         content/browser/tabbrowser.js                 (content/tabbrowser.js)
         content/browser/tabbrowser-tab.js             (content/tabbrowser-tab.js)
         content/browser/tabbrowser-tabs.js            (content/tabbrowser-tabs.js)
         content/browser/utilityOverlay.js             (content/utilityOverlay.js)
         content/browser/webext-panels.js              (content/webext-panels.js)
 *       content/browser/webext-panels.xhtml           (content/webext-panels.xhtml)
--- a/browser/components/newtab/bin/render-activity-stream-html.js
+++ b/browser/components/newtab/bin/render-activity-stream-html.js
@@ -23,16 +23,17 @@ const DEFAULT_OPTIONS = {
  *         {bool} options.debug         Should we use dev versions of JS libraries?
  *         {bool} options.noscripts     Should we include scripts in the prerendered files?
  * @return {str}         An HTML document as a string
  */
 function templateHTML(options) {
   const debugString = options.debug ? "-dev" : "";
   const scripts = [
     "chrome://browser/content/contentSearchUI.js",
+    "chrome://browser/content/contentSearchHandoffUI.js",
     "chrome://browser/content/contentTheme.js",
     `${options.baseUrl}vendor/react${debugString}.js`,
     `${options.baseUrl}vendor/react-dom${debugString}.js`,
     `${options.baseUrl}vendor/prop-types.js`,
     `${options.baseUrl}vendor/redux.js`,
     `${options.baseUrl}vendor/react-redux.js`,
     `${options.baseUrl}vendor/react-transition-group.js`,
     `${options.baseUrl}data/content/activity-stream.bundle.js`,
--- a/browser/components/newtab/content-src/components/Search/Search.jsx
+++ b/browser/components/newtab/content-src/components/Search/Search.jsx
@@ -1,28 +1,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* globals ContentSearchUIController */
+/* globals ContentSearchUIController, ContentSearchHandoffUIController */
 "use strict";
 
 import { actionCreators as ac, actionTypes as at } from "common/Actions.jsm";
 import { connect } from "react-redux";
 import { IS_NEWTAB } from "content-src/lib/constants";
 import React from "react";
 
 export class _Search extends React.PureComponent {
   constructor(props) {
     super(props);
     this.onSearchClick = this.onSearchClick.bind(this);
     this.onSearchHandoffClick = this.onSearchHandoffClick.bind(this);
     this.onSearchHandoffPaste = this.onSearchHandoffPaste.bind(this);
     this.onSearchHandoffDrop = this.onSearchHandoffDrop.bind(this);
     this.onInputMount = this.onInputMount.bind(this);
+    this.onInputMountHandoff = this.onInputMountHandoff.bind(this);
     this.onSearchHandoffButtonMount = this.onSearchHandoffButtonMount.bind(
       this
     );
   }
 
   handleEvent(event) {
     // Also track search events with our own telemetry
     if (event.detail.type === "Search") {
@@ -98,16 +99,24 @@ export class _Search extends React.PureC
       );
       addEventListener("ContentSearchClient", this);
     } else {
       window.gContentSearchController = null;
       removeEventListener("ContentSearchClient", this);
     }
   }
 
+  onInputMountHandoff(input) {
+    if (input) {
+      // The handoff UI controller helps usset the search icon and reacts to
+      // changes to default engine to keep everything in sync.
+      this._handoffSearchController = new ContentSearchHandoffUIController();
+    }
+  }
+
   onSearchHandoffButtonMount(button) {
     // Keep a reference to the button for use during "paste" event handling.
     this._searchHandoffButton = button;
   }
 
   /*
    * Do not change the ID on the input field, as legacy newtab code
    * specifically looks for the id 'newtab-search-text' on input fields
@@ -162,28 +171,20 @@ export class _Search extends React.PureC
               />
               <input
                 type="search"
                 className="fake-editable"
                 tabIndex="-1"
                 aria-hidden="true"
                 onDrop={this.onSearchHandoffDrop}
                 onPaste={this.onSearchHandoffPaste}
+                ref={this.onInputMountHandoff}
               />
               <div className="fake-caret" />
             </button>
-            {/*
-            This dummy and hidden input below is so we can load ContentSearchUIController.
-            Why? It sets --newtab-search-icon for us and it isn't trivial to port over.
-          */}
-            <input
-              type="search"
-              style={{ display: "none" }}
-              ref={this.onInputMount}
-            />
           </div>
         )}
       </div>
     );
   }
 }
 
 export const Search = connect()(_Search);
--- a/browser/components/newtab/data/content/activity-stream.bundle.js
+++ b/browser/components/newtab/data/content/activity-stream.bundle.js
@@ -10281,31 +10281,32 @@ const TopSites = Object(react_redux__WEB
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var content_src_lib_constants__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_3__);
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* globals ContentSearchUIController */
+/* globals ContentSearchUIController, ContentSearchHandoffUIController */
 
 
 
 
 
 
 class _Search extends react__WEBPACK_IMPORTED_MODULE_3___default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onSearchClick = this.onSearchClick.bind(this);
     this.onSearchHandoffClick = this.onSearchHandoffClick.bind(this);
     this.onSearchHandoffPaste = this.onSearchHandoffPaste.bind(this);
     this.onSearchHandoffDrop = this.onSearchHandoffDrop.bind(this);
     this.onInputMount = this.onInputMount.bind(this);
+    this.onInputMountHandoff = this.onInputMountHandoff.bind(this);
     this.onSearchHandoffButtonMount = this.onSearchHandoffButtonMount.bind(this);
   }
 
   handleEvent(event) {
     // Also track search events with our own telemetry
     if (event.detail.type === "Search") {
       this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].UserEvent({
         event: "SEARCH"
@@ -10385,16 +10386,24 @@ class _Search extends react__WEBPACK_IMP
       window.gContentSearchController = new ContentSearchUIController(input, input.parentNode, healthReportKey, searchSource);
       addEventListener("ContentSearchClient", this);
     } else {
       window.gContentSearchController = null;
       removeEventListener("ContentSearchClient", this);
     }
   }
 
+  onInputMountHandoff(input) {
+    if (input) {
+      // The handoff UI controller helps usset the search icon and reacts to
+      // changes to default engine to keep everything in sync.
+      this._handoffSearchController = new ContentSearchHandoffUIController();
+    }
+  }
+
   onSearchHandoffButtonMount(button) {
     // Keep a reference to the button for use during "paste" event handling.
     this._searchHandoffButton = button;
   }
   /*
    * Do not change the ID on the input field, as legacy newtab code
    * specifically looks for the id 'newtab-search-text' on input fields
    * in order to execute searches in various tests
@@ -10436,26 +10445,21 @@ class _Search extends react__WEBPACK_IMP
       className: "fake-textbox",
       "data-l10n-id": "newtab-search-box-search-the-web-text"
     }), react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("input", {
       type: "search",
       className: "fake-editable",
       tabIndex: "-1",
       "aria-hidden": "true",
       onDrop: this.onSearchHandoffDrop,
-      onPaste: this.onSearchHandoffPaste
+      onPaste: this.onSearchHandoffPaste,
+      ref: this.onInputMountHandoff
     }), react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("div", {
       className: "fake-caret"
-    })), react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement("input", {
-      type: "search",
-      style: {
-        display: "none"
-      },
-      ref: this.onInputMount
-    })));
+    }))));
   }
 
 }
 const Search = Object(react_redux__WEBPACK_IMPORTED_MODULE_1__["connect"])()(_Search);
 
 /***/ }),
 /* 77 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
--- a/browser/components/newtab/prerendered/activity-stream-debug.html
+++ b/browser/components/newtab/prerendered/activity-stream-debug.html
@@ -15,16 +15,17 @@
     <link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
     <link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
   </head>
   <body class="activity-stream">
     <div id="header-asrouter-container" role="presentation"></div>
     <div id="root"></div>
     <div id="footer-asrouter-container" role="presentation"></div>
     <script src="chrome://browser/content/contentSearchUI.js"></script>
+    <script src="chrome://browser/content/contentSearchHandoffUI.js"></script>
     <script src="chrome://browser/content/contentTheme.js"></script>
     <script src="resource://activity-stream/vendor/react-dev.js"></script>
     <script src="resource://activity-stream/vendor/react-dom-dev.js"></script>
     <script src="resource://activity-stream/vendor/prop-types.js"></script>
     <script src="resource://activity-stream/vendor/redux.js"></script>
     <script src="resource://activity-stream/vendor/react-redux.js"></script>
     <script src="resource://activity-stream/vendor/react-transition-group.js"></script>
     <script src="resource://activity-stream/data/content/activity-stream.bundle.js"></script>
--- a/browser/components/newtab/prerendered/activity-stream.html
+++ b/browser/components/newtab/prerendered/activity-stream.html
@@ -15,16 +15,17 @@
     <link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
     <link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
   </head>
   <body class="activity-stream">
     <div id="header-asrouter-container" role="presentation"></div>
     <div id="root"></div>
     <div id="footer-asrouter-container" role="presentation"></div>
     <script src="chrome://browser/content/contentSearchUI.js"></script>
+    <script src="chrome://browser/content/contentSearchHandoffUI.js"></script>
     <script src="chrome://browser/content/contentTheme.js"></script>
     <script src="resource://activity-stream/vendor/react.js"></script>
     <script src="resource://activity-stream/vendor/react-dom.js"></script>
     <script src="resource://activity-stream/vendor/prop-types.js"></script>
     <script src="resource://activity-stream/vendor/redux.js"></script>
     <script src="resource://activity-stream/vendor/react-redux.js"></script>
     <script src="resource://activity-stream/vendor/react-transition-group.js"></script>
     <script src="resource://activity-stream/data/content/activity-stream.bundle.js"></script>
--- a/browser/components/newtab/test/unit/unit-entry.js
+++ b/browser/components/newtab/test/unit/unit-entry.js
@@ -68,18 +68,21 @@ const TEST_GLOBAL = {
             this.sendReferrer = sendReferrer;
             this.originalReferrer = originalReferrer;
           };
       }
       return function() {};
     },
     isSuccessCode: () => true,
   },
+  // NB: These are functions/constructors
   // eslint-disable-next-line object-shorthand
-  ContentSearchUIController: function() {}, // NB: This is a function/constructor
+  ContentSearchUIController: function() {},
+  // eslint-disable-next-line object-shorthand
+  ContentSearchHandoffUIController: function() {},
   Cc: {
     "@mozilla.org/browser/nav-bookmarks-service;1": {
       addObserver() {},
       getService() {
         return this;
       },
       removeObserver() {},
       SOURCES: {},
--- a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
@@ -113,19 +113,10 @@ document.addEventListener("DOMContentLoa
     }
   });
   editable.addEventListener("paste", function(ev) {
     ev.preventDefault();
     handoffSearch(ev.clipboardData.getData("Text"));
   });
 
   // Load contentSearchUI so it sets the search engine icon for us.
-  // TODO: FIXME. We should eventually refector contentSearchUI to do only what
-  // we need and have it do the common search handoff work for
-  // about:newtab and about:privatebrowsing.
-  let input = document.getElementById("dummy-input");
-  new window.ContentSearchUIController(
-    input,
-    input.parentNode,
-    "aboutprivatebrowsing",
-    "aboutprivatebrowsing"
-  );
+  new window.ContentSearchHandoffUIController();
 });
--- a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml
@@ -10,17 +10,17 @@
   <head>
     <meta http-equiv="Content-Security-Policy" content="default-src chrome: blob:; object-src 'none'"/>
     <link rel="icon" type="image/png" href="chrome://browser/skin/privatebrowsing/favicon.svg"/>
     <link rel="stylesheet" href="chrome://browser/content/aboutPrivateBrowsing.css" type="text/css" media="all"/>
     <link rel="stylesheet" href="chrome://browser/skin/privatebrowsing/aboutPrivateBrowsing.css" type="text/css" media="all"/>
     <link rel="localization" href="branding/brand.ftl"/>
     <link rel="localization" href="browser/aboutPrivateBrowsing.ftl"/>
     <script src="chrome://browser/content/aboutPrivateBrowsing.js"></script>
-    <script src="chrome://browser/content/contentSearchUI.js"></script>
+    <script src="chrome://browser/content/contentSearchHandoffUI.js"></script>
   </head>
 
   <body>
     <p class="showNormal" data-l10n-id="about-private-browsing-not-private"></p>
     <button id="startPrivateBrowsing"
             class="showNormal" data-l10n-id="privatebrowsingpage-open-private-window-label"></button>
     <div id="search-banner" class="search-banner"
          hidden="true">
@@ -45,17 +45,16 @@
         <div class="wordmark" />
       </div>
       <div class="search-inner-wrapper">
         <button id="search-handoff-button" class="search-handoff-button" tabindex="-1" data-l10n-id="about-private-browsing">
           <div class="fake-textbox" data-l10n-id="about-private-browsing-search-placeholder"></div>
           <input id="fake-editable" class="fake-editable" tabindex="-1" aria-hidden="true" />
           <div class="fake-caret" />
         </button>
-        <input id="dummy-input" class="dummy-input" type="search" />
       </div>
       <div class="info">
         <h1 data-l10n-id="about-private-browsing-info-title"></h1>
         <p data-l10n-id="about-private-browsing-info-description"></p>
         <a id="private-browsing-myths" data-l10n-id="about-private-browsing-info-myths"></a>
       </div>
     </div>
   </body>
--- a/browser/modules/ContentSearch.jsm
+++ b/browser/modules/ContentSearch.jsm
@@ -448,16 +448,27 @@ var ContentSearch = {
   },
 
   _onMessageGetState(msg, data) {
     return this.currentStateObj(msg.target.ownerGlobal).then(state => {
       this._reply(msg, "State", state);
     });
   },
 
+  _onMessageGetEngine(msg, data) {
+    return this.currentStateObj(msg.target.ownerGlobal).then(state => {
+      this._reply(msg, "Engine", {
+        isPrivateWindow: state.isPrivateWindow,
+        engine: state.isPrivateWindow
+          ? state.currentPrivateEngine
+          : state.currentEngine,
+      });
+    });
+  },
+
   _onMessageGetStrings(msg, data) {
     this._reply(msg, "Strings", this.searchSuggestionUIStrings);
   },
 
   _onMessageSearch(msg, data) {
     this.performSearch(msg, data);
   },
 
--- a/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css
+++ b/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css
@@ -158,20 +158,16 @@ p {
   display: none;
   inset-inline-start: 47px;
   height: 17px;
   position: absolute;
   top: 16px;
   width: 1px;
 }
 
-.dummy-input {
-  display: none;
-}
-
 .search-banner {
   width: 100%;
   background-color: var(--in-content-banner-background);
   color: var(--in-content-banner-text-color);
 }
 
 .banner-body {
   margin: auto;