Bug 1354533 - Update the History panelview when it's shown inside the new Library panel. r?Gijs,mak draft
authorMike de Boer <mdeboer@mozilla.com>
Fri, 21 Jul 2017 15:07:23 +0200
changeset 615166 99f6c504b74b66db94f0601719ce610b831d68c2
parent 615011 131e19a573e901fb4d01b471b11b7916420b9fee
child 639089 c8064de4065e49ab727d24f789c702bcd1e0890b
push id70254
push usergijskruitbosch@gmail.com
push dateTue, 25 Jul 2017 15:08:22 +0000
reviewersGijs, mak
bugs1354533
milestone56.0a1
Bug 1354533 - Update the History panelview when it's shown inside the new Library panel. r?Gijs,mak This patch changes the history-panelmenu widget with the following: - Move the Recently Closed Tabs and Recently Closed Windows lists into their own respective (nested) subview, - Add a Recent History header to list of history items, - Extend the list of Recent History items to be max 42 items long, - Share more code with Bookmarks panel, - Generalizes panelview event dispatching to always support customizable widgets. MozReview-Commit-ID: 4sBR6llIvxG
browser/base/content/browser.css
browser/components/customizableui/CustomizableWidgets.jsm
browser/components/customizableui/PanelMultiView.jsm
browser/components/customizableui/content/panelUI.inc.xul
browser/components/places/content/browserPlacesViews.js
browser/locales/en-US/chrome/browser/browser.dtd
browser/themes/shared/menupanel.inc.css
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -485,16 +485,19 @@ toolbar:not(#TabsToolbar) > #personal-bo
 #reload-button[disabled]:not(:-moz-window-inactive) > .toolbarbutton-icon {
   opacity: 1 !important;
 }
 
 #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,
 #panelMenu_bookmarksMenu > .bookmark-item {
   max-width: none;
 }
 
 #main-window:-moz-lwtheme {
   background-repeat: no-repeat;
   background-position: top right;
 }
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -170,22 +170,42 @@ function clearSubview(aSubview) {
 const CustomizableWidgets = [
   {
     id: "history-panelmenu",
     type: "view",
     viewId: "PanelUI-history",
     shortcutId: "key_gotoHistory",
     tooltiptext: "history-panelmenu.tooltiptext2",
     defaultArea: CustomizableUI.AREA_PANEL,
+    recentlyClosedTabsPanel: "appMenu-library-recentlyClosedTabs",
+    recentlyClosedWindowsPanel: "appMenu-library-recentlyClosedWindows",
+    handleEvent(event) {
+      switch (event.type) {
+        case "PanelMultiViewHidden":
+          this.onPanelMultiViewHidden(event);
+          break;
+        case "ViewShowing":
+          this.onSubViewShowing(event);
+          break;
+        default:
+          throw new Error(`Unsupported event for '${this.id}'`);
+      }
+    },
     onViewShowing(aEvent) {
       // Populate our list of history
       const kMaxResults = 15;
       let doc = aEvent.target.ownerDocument;
       let win = doc.defaultView;
 
+      if (AppConstants.MOZ_PHOTON_THEME && win.gPhotonStructure) {
+        // For the Photon panelview we're going to do something different!
+        this.onPhotonViewShowing(aEvent);
+        return;
+      }
+
       let options = PlacesUtils.history.getNewQueryOptions();
       options.excludeQueries = true;
       options.queryType = options.QUERY_TYPE_HISTORY;
       options.sortingMode = options.SORT_BY_DATE_DESCENDING;
       options.maxResults = kMaxResults;
       let query = PlacesUtils.history.getNewQuery();
 
       let items = doc.getElementById("PanelUI-historyItems");
@@ -272,31 +292,102 @@ const CustomizableWidgets = [
       separator.hidden = !elementCount;
       while (--elementCount >= 0) {
         let element = windowsFragment.children[elementCount];
         CustomizableUI.addShortcut(element);
         element.classList.add("subviewbutton", "cui-withicon");
       }
       recentlyClosedWindows.appendChild(windowsFragment);
     },
+    onPhotonViewShowing(event) {
+      if (this._panelMenuView)
+        return;
+
+      let panelview = event.target;
+      let document = panelview.ownerDocument;
+      let window = document.defaultView;
+
+      // We restrict the amount of results to 42. Not 50, but 42. Why? Because 42.
+      let query = "place:queryType=" + Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY +
+        "&sort=" + Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING +
+        "&maxResults=42&excludeQueries=1";
+      this._panelMenuView = new window.PlacesPanelview(document.getElementById("appMenu_historyMenu"),
+        panelview, query);
+      // When either of these sub-subviews show, populate them with recently closed
+      // objects data.
+      document.getElementById(this.recentlyClosedTabsPanel).addEventListener("ViewShowing", this);
+      document.getElementById(this.recentlyClosedWindowsPanel).addEventListener("ViewShowing", this);
+      // When the popup is hidden (thus the panelmultiview node as well), make
+      // sure to stop listening to PlacesDatabase updates.
+      panelview.panelMultiView.addEventListener("PanelMultiViewHidden", this);
+    },
     onCreated(aNode) {
+      // Skip this for the Photon panelview.
+      let doc = aNode.ownerDocument;
+      if (AppConstants.MOZ_PHOTON_THEME && doc.defaultView.gPhotonStructure)
+        return;
+
       // Middle clicking recently closed items won't close the panel - cope:
       let onRecentlyClosedClick = function(aEvent) {
         if (aEvent.button == 1) {
           CustomizableUI.hidePanelForNode(this);
         }
       };
-      let doc = aNode.ownerDocument;
       let recentlyClosedTabs = doc.getElementById("PanelUI-recentlyClosedTabs");
       let recentlyClosedWindows = doc.getElementById("PanelUI-recentlyClosedWindows");
       recentlyClosedTabs.addEventListener("click", onRecentlyClosedClick);
       recentlyClosedWindows.addEventListener("click", onRecentlyClosedClick);
     },
     onViewHiding(aEvent) {
       log.debug("History view is being hidden!");
+    },
+    onPanelMultiViewHidden(event) {
+      let panelMultiView = event.target;
+      let document = panelMultiView.ownerDocument;
+      if (this._panelMenuView) {
+        this._panelMenuView.uninit();
+        delete this._panelMenuView;
+        document.getElementById(this.recentlyClosedTabsPanel).removeEventListener("ViewShowing", this);
+        document.getElementById(this.recentlyClosedWindowsPanel).removeEventListener("ViewShowing", this);
+      }
+      panelMultiView.removeEventListener("PanelMultiViewHidden", this);
+    },
+    onSubViewShowing(event) {
+      let panelview = event.target;
+      let document = event.target.ownerDocument;
+      let window = document.defaultView;
+      let viewType = panelview.id == this.recentlyClosedTabsPanel ? "Tabs" : "Windows";
+
+      this._panelMenuView.clearAllContents(panelview);
+
+      let utils = RecentlyClosedTabsAndWindowsMenuUtils;
+      let method = `get${viewType}Fragment`;
+      let fragment = utils[method](window, "toolbarbutton");
+      let elementCount = fragment.childElementCount;
+      this._panelMenuView._setEmptyPopupStatus(panelview, !elementCount);
+      if (!elementCount)
+        return;
+
+      let body = document.createElement("vbox");
+      body.className = "panel-subview-body";
+      body.appendChild(fragment);
+      let footer;
+      while (--elementCount >= 0) {
+        let element = body.childNodes[elementCount];
+        CustomizableUI.addShortcut(element);
+        element.classList.add("subviewbutton");
+        if (element.classList.contains("restoreallitem")) {
+          footer = element;
+          element.classList.add("panel-subview-footer");
+        } else {
+          element.classList.add("subviewbutton-iconic", "bookmark-item");
+        }
+      }
+      panelview.appendChild(body);
+      panelview.appendChild(footer);
     }
   }, {
     id: "sync-button",
     label: "remotetabs-panelmenu.label",
     tooltiptext: "remotetabs-panelmenu.tooltiptext2",
     type: "view",
     viewId: "PanelUI-remotetabs",
     defaultArea: CustomizableUI.AREA_PANEL,
--- a/browser/components/customizableui/PanelMultiView.jsm
+++ b/browser/components/customizableui/PanelMultiView.jsm
@@ -332,17 +332,17 @@ this.PanelMultiView = class {
     } else {
       this._clickCapturer.removeEventListener("click", this);
     }
     this._panel.removeEventListener("mousemove", this);
     this._panel.removeEventListener("popupshowing", this);
     this._panel.removeEventListener("popupshown", this);
     this._panel.removeEventListener("popuphidden", this);
     this.window.removeEventListener("keydown", this);
-    this.node.dispatchEvent(new this.window.CustomEvent("destructed"));
+    this._dispatchViewEvent(this.node, "destructed");
     this.node = this._clickCapturer = this._viewContainer = this._mainViewContainer =
       this._subViews = this._viewStack = this.__dwu = this._panelViewCache = null;
   }
 
   /**
    * Remove any child subviews into the panelViewCache, to ensure
    * they remain usable even if this panelmultiview instance is removed
    * from the DOM.
@@ -405,18 +405,17 @@ this.PanelMultiView = class {
     } else {
       this._mainViewContainer.appendChild(aNewMainView);
     }
   }
 
   showMainView() {
     if (this.showingSubView) {
       let viewNode = this._currentSubView;
-      let evt = new this.window.CustomEvent("ViewHiding", { bubbles: true, cancelable: true });
-      viewNode.dispatchEvent(evt);
+      this._dispatchViewEvent(viewNode, "ViewHiding");
       if (this.panelViews) {
         viewNode.removeAttribute("current");
         this.showSubView(this._mainViewId);
         this.node.setAttribute("viewtype", "main");
       } else {
         this._transitionHeight(() => {
           viewNode.removeAttribute("current");
           this._currentSubView = null;
@@ -468,47 +467,36 @@ this.PanelMultiView = class {
           // of the main view, i.e. whilst the panel is shown and/ or visible.
           if (!this._mainViewHeight) {
             this._mainViewHeight = previousRect.height;
             this._viewContainer.style.minHeight = this._mainViewHeight + "px";
           }
         }
       }
 
+      this._viewShowing = viewNode;
+
+      // Make sure that new panels always have a title set.
+      if (this.panelViews && aAnchor) {
+        if (!viewNode.hasAttribute("title"))
+          viewNode.setAttribute("title", aAnchor.getAttribute("label"));
+        viewNode.classList.add("PanelUI-subView");
+      }
+      if (this.panelViews && this._mainViewWidth)
+        viewNode.style.maxWidth = viewNode.style.minWidth = this._mainViewWidth + "px";
+
       // Emit the ViewShowing event so that the widget definition has a chance
       // to lazily populate the subview with things.
       let detail = {
         blockers: new Set(),
-        addBlocker(aPromise) {
-          this.blockers.add(aPromise);
-        },
+        addBlocker(promise) {
+          this.blockers.add(promise);
+        }
       };
-
-      // Make sure that new panels always have a title set.
-      let cancel = false;
-      if (this.panelViews && aAnchor) {
-        if (aAnchor && !viewNode.hasAttribute("title"))
-          viewNode.setAttribute("title", aAnchor.getAttribute("label"));
-        viewNode.classList.add("PanelUI-subView");
-        let custWidget = CustomizableWidgets.find(widget => widget.viewId == viewNode.id);
-        if (custWidget) {
-          if (custWidget.onInit)
-            custWidget.onInit(aAnchor);
-          custWidget.onViewShowing({ target: viewNode, preventDefault: () => cancel = true, detail });
-        }
-      }
-      if (this.panelViews && this._mainViewWidth)
-        viewNode.style.maxWidth = viewNode.style.minWidth = this._mainViewWidth + "px";
-
-      this._viewShowing = viewNode;
-      let evt = new window.CustomEvent("ViewShowing", { bubbles: true, cancelable: true, detail });
-      viewNode.dispatchEvent(evt);
-
-      if (!cancel)
-        cancel = evt.defaultPrevented;
+      let cancel = this._dispatchViewEvent(viewNode, "ViewShowing", aAnchor, detail);
       if (detail.blockers.size) {
         try {
           let results = await Promise.all(detail.blockers);
           cancel = cancel || results.some(val => val === false);
         } catch (e) {
           Cu.reportError(e);
           cancel = true;
         }
@@ -538,18 +526,17 @@ this.PanelMultiView = class {
       //
       // All three of these actions make use of CSS transformations, so they
       // should all occur simultaneously.
       if (this.panelViews && playTransition) {
         // Sliding the next subview in means that the previous panelview stays
         // where it is and the active panelview slides in from the left in LTR
         // mode, right in RTL mode.
         let onTransitionEnd = () => {
-          evt = new window.CustomEvent("ViewHiding", { bubbles: true, cancelable: true });
-          previousViewNode.dispatchEvent(evt);
+          this._dispatchViewEvent(previousViewNode, "ViewHiding");
           previousViewNode.removeAttribute("current");
           this.descriptionHeightWorkaround(viewNode);
         };
 
         // There's absolutely no need to show off our epic animation skillz when
         // the panel's not even open.
         if (this._panel.state != "open") {
           onTransitionEnd();
@@ -651,38 +638,70 @@ this.PanelMultiView = class {
 
                 if (!reverse)
                   viewNode.style.removeProperty("margin-inline-start");
                 if (aAnchor)
                   aAnchor.removeAttribute("open");
 
                 this._viewContainer.removeAttribute("transition-reverse");
 
-                viewNode.dispatchEvent(new window.CustomEvent("ViewShown",
-                  { bubbles: true, cancelable: false }));
+                this._dispatchViewEvent(viewNode, "ViewShown");
               }, { once: true });
             });
           }, { once: true });
         });
       } else if (!this.panelViews) {
         this._transitionHeight(() => {
           viewNode.setAttribute("current", true);
           this.node.setAttribute("viewtype", "subview");
           // Now that the subview is visible, we can check the height of the
           // description elements it contains.
           this.descriptionHeightWorkaround(viewNode);
-          viewNode.dispatchEvent(new window.CustomEvent("ViewShown",
-            { bubbles: true, cancelable: false }));
+          this._dispatchViewEvent(viewNode, "ViewShown");
         });
         this._shiftMainView(aAnchor);
       }
     })().catch(e => Cu.reportError(e));
   }
 
   /**
+   * Helper method to emit an event on a panelview, whilst also making sure that
+   * the correct method is called on CustomizableWidget instances.
+   *
+   * @param  {panelview} viewNode  Target of the event to dispatch.
+   * @param  {String}    eventName Name of the event to dispatch.
+   * @param  {DOMNode}   [anchor]  Node where the panel is anchored to. Optional.
+   * @param  {Object}    [detail]  Event detail object. Optional.
+   * @return {Boolean} `true` if the event was canceled by an event handler, `false`
+   *                   otherwise.
+   */
+  _dispatchViewEvent(viewNode, eventName, anchor, detail) {
+    let cancel = false;
+    if (this.panelViews) {
+      let custWidget = CustomizableWidgets.find(widget => widget.viewId == viewNode.id);
+      let method = "on" + eventName;
+      if (custWidget && custWidget[method]) {
+        if (anchor && custWidget.onInit)
+          custWidget.onInit(anchor);
+        custWidget[method]({ target: viewNode, preventDefault: () => cancel = true, detail });
+      }
+    }
+
+    let evt = new this.window.CustomEvent(eventName, {
+      detail,
+      bubbles: true,
+      cancelable: eventName == "ViewShowing"
+    });
+    viewNode.dispatchEvent(evt);
+    if (!cancel)
+      cancel = evt.defaultPrevented;
+    return cancel;
+  }
+
+  /**
    * Calculate the correct bounds of a panelview node offscreen to minimize the
    * amount of paint flashing and keep the stack vs panel layouts from interfering.
    *
    * @param {panelview} viewNode Node to measure the bounds of.
    * @param {Rect}      previousRect Rect representing the previous view
    *                                 (used to fill in any blanks).
    * @param {Function}  callback Called when we got the measurements in and pass
    *                             them on as its first argument.
@@ -962,16 +981,17 @@ this.PanelMultiView = class {
         }
 
         // Always try to layout the panel normally when reopening it. This is
         // also the layout that will be used in customize mode.
         if (this._mainView.hasAttribute("blockinboxworkaround")) {
           this._mainView.style.removeProperty("height");
           this._mainView.removeAttribute("exceeding");
         }
+        this._dispatchViewEvent(this.node, "PanelMultiViewHidden");
         break;
     }
   }
 
   /**
    * Allow for navigating subview buttons using the arrow keys and the Enter key.
    * The Up and Down keys can be used to navigate the list up and down and the
    * Enter, Right or Left - depending on the text direction - key can be used to
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -79,42 +79,83 @@
     </panelview>
 
     <panelview id="PanelUI-history" flex="1">
       <label value="&appMenuHistory.label;" class="panel-subview-header"/>
       <vbox class="panel-subview-body">
         <toolbarbutton id="appMenuViewHistorySidebar"
                        label="&appMenuHistory.viewSidebar.label;"
                        type="checkbox"
+#ifndef MOZ_PHOTON_THEME
                        class="subviewbutton"
+#else
+                       class="subviewbutton subviewbutton-iconic"
+#endif
                        key="key_gotoHistory"
                        oncommand="SidebarUI.toggle('viewHistorySidebar'); PanelUI.hide();">
           <observes element="viewHistorySidebar" attribute="checked"/>
         </toolbarbutton>
         <toolbarbutton id="appMenuClearRecentHistory"
                        label="&appMenuHistory.clearRecent.label;"
+#ifndef MOZ_PHOTON_THEME
                        class="subviewbutton"
+#else
+                       class="subviewbutton subviewbutton-iconic"
+#endif
                        command="Tools:Sanitize"/>
         <toolbarbutton id="appMenuRestoreLastSession"
                        label="&appMenuHistory.restoreSession.label;"
+#ifndef MOZ_PHOTON_THEME
                        class="subviewbutton"
+#else
+                       class="subviewbutton subviewbutton-iconic"
+#endif
                        command="Browser:RestoreLastSession"/>
+#ifndef MOZ_PHOTON_THEME
         <menuseparator id="PanelUI-recentlyClosedTabs-separator"/>
         <vbox id="PanelUI-recentlyClosedTabs" tooltip="bhTooltip"/>
         <menuseparator id="PanelUI-recentlyClosedWindows-separator"/>
         <vbox id="PanelUI-recentlyClosedWindows" tooltip="bhTooltip"/>
         <menuseparator id="PanelUI-historyItems-separator"/>
         <vbox id="PanelUI-historyItems" tooltip="bhTooltip"/>
+#else
+        <toolbarseparator/>
+        <toolbarbutton id="appMenuRecentlyClosedTabs"
+                       label="&historyUndoMenu.label;"
+                       class="subviewbutton subviewbutton-iconic subviewbutton-nav"
+                       closemenu="none"
+                       oncommand="PanelUI.showSubView('appMenu-library-recentlyClosedTabs', this)"/>
+        <toolbarbutton id="appMenuRecentlyClosedWindows"
+                       label="&historyUndoWindowMenu.label;"
+                       class="subviewbutton subviewbutton-iconic subviewbutton-nav"
+                       closemenu="none"
+                       oncommand="PanelUI.showSubView('appMenu-library-recentlyClosedWindows', this)"/>
+        <toolbarseparator/>
+        <label value="&appMenuHistory.recentHistory.label;"
+               class="subview-subheader"/>
+        <toolbaritem id="appMenu_historyMenu"
+                     orient="vertical"
+                     smoothscroll="false"
+                     flatList="true"
+                     tooltip="bhTooltip">
+          <!-- history menu items will go here -->
+        </toolbaritem>
+#endif
       </vbox>
       <toolbarbutton id="PanelUI-historyMore"
                      class="panel-subview-footer subviewbutton"
                      label="&appMenuHistory.showAll.label;"
                      oncommand="PlacesCommandHook.showPlacesOrganizer('History'); CustomizableUI.hidePanelForNode(this);"/>
     </panelview>
 
+#ifdef MOZ_PHOTON_THEME
+    <panelview id="appMenu-library-recentlyClosedTabs"/>
+    <panelview id="appMenu-library-recentlyClosedWindows"/>
+#endif
+
     <panelview id="PanelUI-remotetabs" flex="1" class="PanelUI-subView"
                descriptionheightworkaround="true">
       <label value="&appMenuRemoteTabs.label;" class="panel-subview-header"/>
       <vbox class="panel-subview-body">
         <!-- this widget has 3 boxes in the body, but only 1 is ever visible -->
         <!-- When Sync is ready to sync -->
         <vbox id="PanelUI-remotetabs-main" observes="sync-syncnow-state">
           <vbox id="PanelUI-remotetabs-buttons">
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -227,16 +227,23 @@ PlacesViewBase.prototype = {
     window.updateCommands("places");
     return this.controller.buildContextMenu(aPopup);
   },
 
   destroyContextMenu: function PVB_destroyContextMenu(aPopup) {
     this._contextMenuShown = null;
   },
 
+  clearAllContents(aPopup) {
+    while (aPopup.firstChild) {
+      aPopup.firstChild.remove();
+    }
+    aPopup._emptyMenuitem = aPopup._startMarker = aPopup._endMarker = null;
+  },
+
   _cleanPopup: function PVB_cleanPopup(aPopup, aDelay) {
     // Ensure markers are here when `invalidateContainer` is called before the
     // popup is shown, which may the case for panelviews, for example.
     this._ensureMarkers(aPopup);
     // Remove Places nodes from the popup.
     let child = aPopup._startMarker;
     while (child.nextSibling != aPopup._endMarker) {
       let sibling = child.nextSibling;
@@ -1968,17 +1975,17 @@ PlacesPanelMenuView.prototype = {
     }
 
     for (let i = 0; i < this._resultNode.childCount; ++i) {
       this._insertNewItem(this._resultNode.getChild(i), null);
     }
   }
 };
 
-class PlacesPanelview extends PlacesViewBase {
+var PlacesPanelview = class extends PlacesViewBase {
   constructor(container, panelview, place, options = {}) {
     options.rootElt = container;
     options.viewElt = panelview;
     super(place, options);
     this._viewElt._placesView = this;
     // We're simulating a popup show, because a panelview may only be shown when
     // its containing popup is already shown.
     this._onPopupShowing({ originalTarget: this._viewElt });
@@ -2106,19 +2113,22 @@ class PlacesPanelview extends PlacesView
       panelview._emptyMenuitem.className = "subviewbutton";
       if (typeof this.options.extraClasses.entry == "string")
         panelview._emptyMenuitem.classList.add(this.options.extraClasses.entry);
     }
 
     if (empty) {
       panelview.setAttribute("emptyplacesresult", "true");
       // Don't add the menuitem if there is static content.
-      if (!panelview._startMarker.previousSibling &&
-          !panelview._endMarker.nextSibling)
+      // We also support external usage for custom crafted panels - which'll have
+      // no markers present.
+      if (!panelview._startMarker ||
+          (!panelview._startMarker.previousSibling && !panelview._endMarker.nextSibling)) {
         panelview.insertBefore(panelview._emptyMenuitem, panelview._endMarker);
+      }
     } else {
       panelview.removeAttribute("emptyplacesresult");
       try {
         panelview.removeChild(panelview._emptyMenuitem);
       } catch (ex) {}
     }
   }
 
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -347,16 +347,17 @@ These should match what Safari and other
 <!ENTITY appMenuCustomize.tooltip "Customize the Menu and Toolbars">
 <!ENTITY appMenuCustomizeExit.label "Exit Customize">
 <!ENTITY appMenuCustomizeExit.tooltip "Finish Customizing">
 <!ENTITY appMenuHistory.label "History">
 <!ENTITY appMenuHistory.showAll.label "Show All History">
 <!ENTITY appMenuHistory.clearRecent.label "Clear Recent History…">
 <!ENTITY appMenuHistory.restoreSession.label "Restore Previous Session">
 <!ENTITY appMenuHistory.viewSidebar.label "View History Sidebar">
+<!ENTITY appMenuHistory.recentHistory.label "Recent History">
 <!ENTITY appMenuHelp.label "Help">
 <!ENTITY appMenuHelp.tooltip "Open Help Menu">
 
 <!ENTITY appMenuRemoteTabs.label "Synced Tabs">
 <!-- LOCALIZATION NOTE (appMenuRemoteTabs.notabs.label): This is shown beneath
      the name of a device when that device has no open tabs -->
 <!ENTITY appMenuRemoteTabs.notabs.label "No open tabs">
 <!-- LOCALIZATION NOTE (appMenuRemoteTabs.showMore.label, appMenuRemoteTabs.showMore.tooltip):
--- a/browser/themes/shared/menupanel.inc.css
+++ b/browser/themes/shared/menupanel.inc.css
@@ -178,17 +178,17 @@ toolbarpaletteitem[place="palette"] > #z
 }
 %endif
 
 #add-share-provider {
   list-style-image: url(chrome://browser/skin/menuPanel-small.svg);
   -moz-image-region: rect(0px, 96px, 16px, 80px);
 }
 
-
+#appMenuRecentlyClosedWindows,
 #appMenu-new-window-button {
   list-style-image: url(chrome://browser/skin/new-window.svg);
 }
 
 #appMenu-private-window-button {
   list-style-image: url(chrome://browser/skin/privateBrowsing.svg);
 }
 
@@ -252,16 +252,17 @@ toolbarpaletteitem[place="palette"] > #z
 #appMenu-fullscreen-button[checked] {
   list-style-image: url(chrome://browser/skin/fullscreen-exit.svg);
 }
 
 #appMenu-library-history-button {
   list-style-image: url(chrome://browser/skin/history.svg);
 }
 
+#appMenuRecentlyClosedTabs,
 #appMenu-library-remotetabs-button {
   list-style-image: url("chrome://browser/skin/synced-tabs.svg");
 }
 
 #PanelUI-remotetabs-syncnow {
   list-style-image: url("chrome://browser/skin/sync.svg");
 }
 
@@ -284,9 +285,17 @@ toolbarpaletteitem[place="palette"] > #z
   list-style-image: url("chrome://browser/skin/bookmark.svg");
 }
 
 %ifdef MOZ_PHOTON_THEME
 #bookmarks-menu-button[cui-areatype="menu-panel"],
 toolbarpaletteitem[place="palette"] > #bookmarks-menu-button {
   list-style-image: url("chrome://browser/skin/bookmark-star-on-tray.svg");
 }
+
+#appMenuClearRecentHistory {
+  list-style-image: url("chrome://browser/skin/forget.svg");
+}
+
+#appMenuRestoreLastSession {
+  list-style-image: url("chrome://browser/skin/reload.svg");
+}
 %endif