Bug 1354536 - Part 3 - When the Library view is shown, populate a new 'Recent Highlights' section with at most 12 items as in about:newtab. r?Gijs draft
authorMike de Boer <mdeboer@mozilla.com>
Tue, 19 Sep 2017 16:17:16 +0200
changeset 666986 14126eaa8e19efdc479c745486bfba33d78b81e7
parent 666985 b7c236afd29f9422bb08383ada8e0391853fc4c8
child 732264 420bb91b38b3d377e702e69401bce6eb591470b1
push id80579
push usermdeboer@mozilla.com
push dateTue, 19 Sep 2017 14:22:45 +0000
reviewersGijs
bugs1354536
milestone57.0a1
Bug 1354536 - Part 3 - When the Library view is shown, populate a new 'Recent Highlights' section with at most 12 items as in about:newtab. r?Gijs MozReview-Commit-ID: Bs1RzL0uewH
browser/app/profile/firefox.js
browser/base/content/browser.css
browser/components/customizableui/content/panelUI.inc.xul
browser/components/customizableui/content/panelUI.js
browser/themes/shared/customizableui/panelUI.inc.css
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1271,16 +1271,18 @@ pref("browser.newtabpage.columns", 5);
 // directory tiles download URL
 pref("browser.newtabpage.directory.source", "https://tiles.services.mozilla.com/v3/links/fetch/%LOCALE%/%CHANNEL%");
 
 // activates Activity Stream
 pref("browser.newtabpage.activity-stream.enabled", true);
 pref("browser.newtabpage.activity-stream.prerender", true);
 pref("browser.newtabpage.activity-stream.aboutHome.enabled", true);
 
+pref("browser.library.activity-stream.enabled", true);
+
 // Enable the DOM fullscreen API.
 pref("full-screen-api.enabled", true);
 
 // Startup Crash Tracking
 // number of startup crashes that can occur before starting into safe mode automatically
 // (this pref has no effect if more than 6 hours have passed since the last crash)
 pref("toolkit.startup.max_resumed_crashes", 3);
 
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -500,16 +500,17 @@ toolbar:not(#TabsToolbar) > #personal-bo
 
 #PanelUI-feeds > .feed-toolbarbutton:-moz-locale-dir(rtl) {
   direction: rtl;
 }
 
 #appMenu_historyMenu > .bookmark-item,
 #appMenu-library-recentlyClosedTabs > .panel-subview-body > .bookmark-item,
 #appMenu-library-recentlyClosedWindows > .panel-subview-body > .bookmark-item,
+#appMenu-library-recentHighlights > .bookmark-item,
 #panelMenu_bookmarksMenu > .bookmark-item {
   max-width: none;
 }
 
 #main-window:-moz-lwtheme {
   background-repeat: no-repeat;
   background-position: top right;
 }
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -716,16 +716,26 @@
                        label="&libraryDownloads.label;"
                        closemenu="none"
                        oncommand="DownloadsSubview.show(this);"/>
         <toolbarbutton id="appMenu-library-remotetabs-button"
                        class="subviewbutton subviewbutton-iconic subviewbutton-nav"
                        label="&appMenuRemoteTabs.label;"
                        closemenu="none"
                        oncommand="PanelUI.showSubView('PanelUI-remotetabs', this)"/>
+        <toolbarseparator/>
+        <label value="&appMenuRecentHighlights.label;"
+               class="subview-subheader"/>
+        <toolbaritem id="appMenu-library-recentHighlights"
+                     orient="vertical"
+                     smoothscroll="false"
+                     flatList="true"
+                     tooltip="bhTooltip">
+          <!-- Recent Highlights will go here -->
+        </toolbaritem>
       </vbox>
     </panelview>
 
     <panelview id="PanelUI-bookmarkingTools" class="PanelUI-subView">
       <vbox class="panel-subview-body">
         <toolbarbutton id="panelMenu_toggleBookmarksMenu"
                        label="&addBookmarksMenu.label;"
                        label-checked="&removeBookmarksMenu.label;"
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -1,16 +1,18 @@
 /* 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/. */
 
+XPCOMUtils.defineLazyModuleGetter(this, "AppMenuNotifications",
+                                  "resource://gre/modules/AppMenuNotifications.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
+                                  "resource://gre/modules/NewTabUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ScrollbarSampler",
                                   "resource:///modules/ScrollbarSampler.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "AppMenuNotifications",
-                                  "resource://gre/modules/AppMenuNotifications.jsm");
 
 /**
  * Maintains the state and dispatches events for the main menu panel.
  */
 
 const PanelUI = {
   /** Panel events that we listen for. **/
   get kEvents() {
@@ -20,16 +22,17 @@ const PanelUI = {
    * Used for lazily getting and memoizing elements from the document. Lazy
    * getters are set in init, and memoizing happens after the first retrieval.
    */
   get kElements() {
     return {
       mainView: "appMenu-mainView",
       multiView: "appMenu-multiView",
       helpView: "PanelUI-helpView",
+      libraryView: "appMenu-libraryView",
       menuButton: "PanelUI-menu-button",
       panel: "appMenu-popup",
       notificationPanel: "appMenu-notification-popup",
       addonNotificationContainer: "appMenu-addon-banners",
       overflowFixedList: "widget-overflow-fixed-list",
       overflowPanel: "widget-overflow",
       navbar: "nav-bar",
     };
@@ -70,16 +73,22 @@ const PanelUI = {
 
     if (this.autoHideToolbarInFullScreen) {
       window.addEventListener("fullscreen", this);
     } else {
       window.addEventListener("MozDOMFullscreen:Entered", this);
       window.addEventListener("MozDOMFullscreen:Exited", this);
     }
 
+    XPCOMUtils.defineLazyPreferenceGetter(this, "libraryRecentHighlightsEnabled",
+      "browser.library.activity-stream.enabled", false, (pref, previousValue, newValue) => {
+        if (!newValue)
+          this.clearLibraryRecentHighlights();
+      });
+
     window.addEventListener("activate", this);
     window.matchMedia("(-moz-overlay-scrollbars)").addListener(this._overlayScrollListenerBoundFn);
     CustomizableUI.addListener(this);
 
     for (let event of this.kEvents) {
       this.notificationPanel.addEventListener(event, this);
     }
 
@@ -145,16 +154,17 @@ const PanelUI = {
     window.removeEventListener("MozDOMFullscreen:Exited", this);
     window.removeEventListener("fullscreen", this);
     window.removeEventListener("activate", this);
     this.menuButton.removeEventListener("mousedown", this);
     this.menuButton.removeEventListener("keypress", this);
     window.matchMedia("(-moz-overlay-scrollbars)").removeListener(this._overlayScrollListenerBoundFn);
     CustomizableUI.removeListener(this);
     this._overlayScrollListenerBoundFn = null;
+    this.libraryView.removeEventListener("ViewShowing", this);
   },
 
   /**
    * Customize mode extracts the mainView and puts it somewhere else while the
    * user customizes. Upon completion, this function can be called to put the
    * panel back to where it belongs in normal browsing mode.
    *
    * @param aMainView
@@ -288,16 +298,21 @@ const PanelUI = {
         this.toggle(aEvent);
         break;
       case "MozDOMFullscreen:Entered":
       case "MozDOMFullscreen:Exited":
       case "fullscreen":
       case "activate":
         this._updateNotifications();
         break;
+      case "ViewShowing":
+        if (aEvent.target == this.libraryView) {
+          this.onLibraryViewShowing(aEvent.target);
+        }
+        break;
     }
   },
 
   get isReady() {
     return !!this._isReady;
   },
 
   get isNotificationPanelOpen() {
@@ -379,16 +394,18 @@ const PanelUI = {
       return;
     }
 
     if (!aAnchor) {
       Cu.reportError("Expected an anchor when opening subview with id: " + aViewId);
       return;
     }
 
+    this.ensureLibraryInitialized(viewNode);
+
     let container = aAnchor.closest("panelmultiview,photonpanelmultiview");
     if (container) {
       container.showSubView(aViewId, aAnchor);
     } else if (!aAnchor.open) {
       aAnchor.open = true;
 
       let tempPanel = document.createElement("panel");
       tempPanel.setAttribute("type", "arrow");
@@ -477,16 +494,105 @@ const PanelUI = {
       tempPanel.openPopup(anchor, {
         position: "bottomcenter topright",
         triggerEvent: domEvent,
       });
     }
   },
 
   /**
+   * Sets up the event listener for when the Library view is shown.
+   *
+   * @param {panelview} viewNode The library view.
+   */
+  ensureLibraryInitialized(viewNode) {
+    if (viewNode != this.libraryView || viewNode._initialized)
+      return;
+
+    viewNode._initialized = true;
+    viewNode.addEventListener("ViewShowing", this);
+  },
+
+  /**
+   * When the Library view is showing, we can start fetching and populating the
+   * list of Recent Highlights.
+   * This is done asynchronously and may finish when the view is already visible.
+   *
+   * @param {panelview} viewNode The library view.
+   */
+  async onLibraryViewShowing(viewNode) {
+    if (this._loadingRecentHighlights) {
+      return;
+    }
+    this._loadingRecentHighlights = true;
+
+    // Since the library is the first view shown, we don't want to add a blocker
+    // to the event, which would make PanelMultiView wait to show it.
+    let container = this.clearLibraryRecentHighlights();
+    if (!this.libraryRecentHighlightsEnabled) {
+      this._loadingRecentHighlights = false;
+      return;
+    }
+
+    let highlights = await NewTabUtils.activityStreamLinks.getHighlights({ withFavicons: true });
+    // If there's nothing to display, or the panel is already hidden, get out.
+    if (!highlights.length || viewNode.panelMultiView.getAttribute("panelopen") != "true") {
+      this._loadingRecentHighlights = false;
+      return;
+    }
+
+    container.hidden = container.previousSibling.hidden =
+      container.previousSibling.previousSibling.hidden = false;
+    let fragment = document.createDocumentFragment();
+    for (let highlight of highlights) {
+      let button = document.createElement("toolbarbutton");
+      button.classList.add("subviewbutton", "highlight", "subviewbutton-iconic", "bookmark-item");
+      let title = highlight.title || highlight.url;
+      button.setAttribute("label", title);
+      button.setAttribute("tooltiptext", title);
+      button.setAttribute("type", "highlight-" + highlight.type);
+      button.setAttribute("onclick", "PanelUI.onLibraryHighlightClick(event)");
+      if (highlight.favicon) {
+        button.setAttribute("image", highlight.favicon);
+      }
+      button._highlight = highlight;
+      fragment.appendChild(button);
+    }
+    container.appendChild(fragment);
+
+    this._loadingRecentHighlights = false;
+  },
+
+  /**
+   * Remove all the nodes from the 'Recent Highlights' section and hide it as well.
+   */
+  clearLibraryRecentHighlights() {
+    let container = document.getElementById("appMenu-library-recentHighlights")
+    while (container.firstChild) {
+      container.firstChild.remove();
+    }
+    container.hidden = container.previousSibling.hidden =
+      container.previousSibling.previousSibling.hidden = true;
+    return container;
+  },
+
+  /**
+   * Event handler; invoked when an item of the Recent Highlights is clicked.
+   *
+   * @param {MouseEvent} event Click event, originating from the Highlight.
+   */
+  onLibraryHighlightClick(event) {
+    let button = event.target;
+    if (event.button > 1 || !button._highlight) {
+      return;
+    }
+    window.openUILink(button._highlight.url, event);
+  },
+
+  /**
    * NB: The enable- and disableSingleSubviewPanelAnimations methods only
    * affect the hiding/showing animations of single-subview panels (tempPanel
    * in the showSubView method).
    */
   disableSingleSubviewPanelAnimations() {
     this._disableAnimations = true;
   },
 
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -1301,16 +1301,34 @@ panelview .toolbarbutton-1,
 }
 
 .subviewbutton[shortcut]::after,
 .subviewbutton[shortcut]::after,
 .PanelUI-subView .subviewbutton-nav::after {
   margin-inline-start: 10px;
 }
 
+.subviewbutton[type="highlight-bookmark"]::after {
+  content: url("chrome://browser/skin/bookmark-hollow.svg");
+}
+
+.subviewbutton[type="highlight-history"]::after {
+  content: url("chrome://browser/skin/history.svg");
+}
+
+.subviewbutton[type="highlight-bookmark"]::after,
+.subviewbutton[type="highlight-history"]::after {
+  -moz-context-properties: fill;
+  fill: GrayText;
+  float: right;
+  opacity: .5;
+  /* Centers the icon and resizes it to 12px square. */
+  transform: translateY(2px) scaleX(.75);
+}
+
 /* This is a <label> but it should fit in with the menu font- and colorwise. */
 #PanelUI-characterEncodingView-autodetect-label {
   font: menu;
   color: inherit;
 }
 
 /* START photon adjustments */