Backed out changeset b5a1430d99b5 (bug 1403530)for failing clipboard on devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js on a CLOSED TREE
authorAndreea Pavel <apavel@mozilla.com>
Thu, 26 Apr 2018 14:52:23 +0300
changeset 471815 56084ee1029acf0a608eb8122d5451354ddee7fd
parent 471814 6e69352a940e1052f275518256249308f170a5f7
child 471816 0be8ef04ed899478df11701f8fe23b839bbbdabd
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1403530
milestone61.0a1
backs outb5a1430d99b50108bf7f1f44ec9c29f0a544fd7c
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
Backed out changeset b5a1430d99b5 (bug 1403530)for failing clipboard on devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js on a CLOSED TREE
devtools/client/jar.mn
devtools/client/locales/en-US/netmonitor.properties
devtools/client/netmonitor/src/assets/icons/drop-down.svg
devtools/client/netmonitor/src/assets/styles/Toolbar.css
devtools/client/netmonitor/src/assets/styles/variables.css
devtools/client/netmonitor/src/components/Toolbar.js
devtools/client/netmonitor/src/har/har-menu-utils.js
devtools/client/netmonitor/src/har/moz.build
devtools/client/netmonitor/src/utils/menu.js
devtools/client/netmonitor/src/widgets/RequestListContextMenu.js
devtools/client/netmonitor/src/widgets/RequestListHeaderContextMenu.js
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -295,17 +295,16 @@ devtools.jar:
     content/netmonitor/src/assets/styles/netmonitor.css (netmonitor/src/assets/styles/netmonitor.css)
     content/netmonitor/src/assets/styles/NetworkDetailsPanel.css (netmonitor/src/assets/styles/NetworkDetailsPanel.css)
     content/netmonitor/src/assets/styles/RequestList.css (netmonitor/src/assets/styles/RequestList.css)
     content/netmonitor/src/assets/styles/StatisticsPanel.css (netmonitor/src/assets/styles/StatisticsPanel.css)
     content/netmonitor/src/assets/styles/StatusBar.css (netmonitor/src/assets/styles/StatusBar.css)
     content/netmonitor/src/assets/styles/Toolbar.css (netmonitor/src/assets/styles/Toolbar.css)
     content/netmonitor/src/assets/styles/variables.css (netmonitor/src/assets/styles/variables.css)
     content/netmonitor/src/assets/icons/play.svg (netmonitor/src/assets/icons/play.svg)
-    content/netmonitor/src/assets/icons/drop-down.svg (netmonitor/src/assets/icons/drop-down.svg)
     content/netmonitor/index.html (netmonitor/index.html)
     content/netmonitor/initializer.js (netmonitor/initializer.js)
 
     # Application panel
     content/application/index.html (application/index.html)
     content/application/initializer.js (application/initializer.js)
 
     # Devtools-components
--- a/devtools/client/locales/en-US/netmonitor.properties
+++ b/devtools/client/locales/en-US/netmonitor.properties
@@ -938,16 +938,17 @@ netmonitor.context.copyImageAsDataUri.ac
 # LOCALIZATION NOTE (netmonitor.context.saveImageAs): This is the label displayed
 # on the context menu that save the Image
 netmonitor.context.saveImageAs=Save Image As
 
 # LOCALIZATION NOTE (netmonitor.context.saveImageAs.accesskey): This is the access key
 # for the Copy Image As Data URI menu item displayed in the context menu for a request
 netmonitor.context.saveImageAs.accesskey=V
 
+
 # LOCALIZATION NOTE (netmonitor.context.copyAllAsHar): This is the label displayed
 # on the context menu that copies all as HAR format
 netmonitor.context.copyAllAsHar=Copy All As HAR
 
 # LOCALIZATION NOTE (netmonitor.context.copyAllAsHar.accesskey): This is the access key
 # for the Copy All As HAR menu item displayed in the context menu for a network panel
 netmonitor.context.copyAllAsHar.accesskey=O
 
@@ -1057,12 +1058,8 @@ netmonitor.status.tooltip.worker = %1$S 
 # of the column status code, when the request is cached and is from a service worker
 # %1$S is the status code, %2$S is the status text.
 netmonitor.status.tooltip.cachedworker = %1$S %2$S (cached, service worker)
 
 # LOCALIZATION NOTE (netmonitor.label.dropHarFiles): This is a label
 # rendered within the Network panel when *.har file(s) are dragged
 # over the content.
 netmonitor.label.dropHarFiles = Drop HAR files here
-
-# LOCALIZATION NOTE (netmonitor.label.har): This is a label used
-# as a tooltip for toolbar drop-down button with HAR actions
-netmonitor.label.har=HAR Export/Import
deleted file mode 100644
--- a/devtools/client/netmonitor/src/assets/icons/drop-down.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-<!-- 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/. -->
-<svg xmlns="http://www.w3.org/2000/svg"
-     width="8" height="8" viewBox="0 0 16 16">
-  <path fill="context-fill" d="M7.9 16.3c-.3 0-.6-.1-.8-.4l-4-4.8c-.2-.3-.3-.5-.1-.8.1-.3.5-.3.9-.3h8c.4 0 .7 0 .9.3.2.4.1.6-.1.9l-4 4.8c-.2.3-.5.3-.8.3zM7.8 0c.3 0 .6.1.7.4L12.4 5c.2.3.3.4.1.7-.1.4-.5.3-.8.3H3.9c-.4 0-.8.1-.9-.2-.2-.4-.1-.6.1-.9L7 .3c.2-.3.5-.3.8-.3z"/>
-</svg>
-
--- a/devtools/client/netmonitor/src/assets/styles/Toolbar.css
+++ b/devtools/client/netmonitor/src/assets/styles/Toolbar.css
@@ -56,36 +56,16 @@
 .devtools-button.devtools-pause-icon::before {
   background-image: var(--pause-icon-url);
 }
 
 .devtools-button.devtools-play-icon::before {
   background-image: var(--play-icon-url);
 }
 
-/* HAR button in the toolbar has a background only when hovered. */
-.devtools-button.devtools-har-button:not(:hover) {
-  background: transparent;
-}
-
-/* HAR button has label and icon, so make sure they don't overlap */
-.devtools-button.devtools-har-button::before {
-  content: "HAR";
-  width: 21px;
-  padding-right: 12px;
-  background-image: var(--drop-down-icon-url);
-  background-position: right center;
-  fill: var(--theme-toolbar-photon-icon-color);
-}
-
-/* Make sure the HAR button label is vertically centered on Mac */
-:root[platform="mac"] .devtools-button.devtools-har-button::before {
-  height: 14px;
-}
-
 .devtools-checkbox {
   position: relative;
   vertical-align: middle;
   bottom: 1px;
 }
 
 .devtools-checkbox-label {
   margin-inline-start: 10px;
--- a/devtools/client/netmonitor/src/assets/styles/variables.css
+++ b/devtools/client/netmonitor/src/assets/styles/variables.css
@@ -35,17 +35,16 @@
 }
 
 :root {
   --primary-toolbar-height: 29px;
 
   /* Icons */
   --play-icon-url: url("chrome://devtools/content/netmonitor/src/assets/icons/play.svg");
   --pause-icon-url: url("chrome://devtools/skin/images/pause.svg");
-  --drop-down-icon-url: url("chrome://devtools/content/netmonitor/src/assets/icons/drop-down.svg");
 
   /* HTTP status codes */
   --status-code-color-1xx: var(--theme-highlight-blue);
   --status-code-color-2xx: var(--theme-highlight-green);
   --status-code-color-3xx: transparent;
   --status-code-color-4xx: var(--theme-highlight-pink);
   --status-code-color-5xx: var(--theme-highlight-red);
 }
--- a/devtools/client/netmonitor/src/components/Toolbar.js
+++ b/devtools/client/netmonitor/src/components/Toolbar.js
@@ -8,17 +8,16 @@ const Services = require("Services");
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { connect } = require("devtools/client/shared/redux/visibility-handler-connect");
 
 const Actions = require("../actions/index");
 const { FILTER_SEARCH_DELAY, FILTER_TAGS } = require("../constants");
 const {
-  getDisplayedRequests,
   getRecordingState,
   getTypeFilteredRequests,
 } = require("../selectors/index");
 const { autocompleteProvider } = require("../utils/filter-autocomplete-provider");
 const { L10N } = require("../utils/l10n");
 const { fetchNetworkUpdatePacket } = require("../utils/request-utils");
 
 // Components
@@ -26,49 +25,42 @@ const SearchBox = createFactory(require(
 
 const { button, div, input, label, span } = dom;
 
 // Localization
 const SEARCH_KEY_SHORTCUT = L10N.getStr("netmonitor.toolbar.filterFreetext.key");
 const SEARCH_PLACE_HOLDER = L10N.getStr("netmonitor.toolbar.filterFreetext.label");
 const TOOLBAR_CLEAR = L10N.getStr("netmonitor.toolbar.clear");
 const TOOLBAR_TOGGLE_RECORDING = L10N.getStr("netmonitor.toolbar.toggleRecording");
-const TOOLBAR_HAR_BUTTON = L10N.getStr("netmonitor.label.har");
 
 // Preferences
 const DEVTOOLS_DISABLE_CACHE_PREF = "devtools.cache.disabled";
 const DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF = "devtools.netmonitor.persistlog";
 const TOOLBAR_FILTER_LABELS = FILTER_TAGS.concat("all").reduce((o, tag) =>
   Object.assign(o, { [tag]: L10N.getStr(`netmonitor.toolbar.filter.${tag}`) }), {});
 const ENABLE_PERSISTENT_LOGS_TOOLTIP =
   L10N.getStr("netmonitor.toolbar.enablePersistentLogs.tooltip");
 const ENABLE_PERSISTENT_LOGS_LABEL =
   L10N.getStr("netmonitor.toolbar.enablePersistentLogs.label");
 const DISABLE_CACHE_TOOLTIP = L10N.getStr("netmonitor.toolbar.disableCache.tooltip");
 const DISABLE_CACHE_LABEL = L10N.getStr("netmonitor.toolbar.disableCache.label");
 
-// Menu
-loader.lazyRequireGetter(this, "showMenu", "devtools/client/netmonitor/src/utils/menu", true);
-loader.lazyRequireGetter(this, "HarMenuUtils", "devtools/client/netmonitor/src/har/har-menu-utils", true);
-
 /**
  * Network monitor toolbar component.
  *
  * Toolbar contains a set of useful tools to control network requests
  * as well as set of filters for filtering the content.
  */
 class Toolbar extends Component {
   static get propTypes() {
     return {
       connector: PropTypes.object.isRequired,
       toggleRecording: PropTypes.func.isRequired,
       recording: PropTypes.bool.isRequired,
       clearRequests: PropTypes.func.isRequired,
-      // List of currently displayed requests (i.e. filtered & sorted).
-      displayedRequests: PropTypes.array.isRequired,
       requestFilterTypes: PropTypes.object.isRequired,
       setRequestFilterText: PropTypes.func.isRequired,
       enablePersistentLogs: PropTypes.func.isRequired,
       togglePersistentLogs: PropTypes.func.isRequired,
       persistentLogsEnabled: PropTypes.bool.isRequired,
       disableBrowserCache: PropTypes.func.isRequired,
       toggleBrowserCache: PropTypes.func.isRequired,
       browserCacheDisabled: PropTypes.bool.isRequired,
@@ -254,57 +246,16 @@ class Toolbar extends Component {
           onChange: toggleBrowserCache,
         }),
         DISABLE_CACHE_LABEL,
       )
     );
   }
 
   /**
-   * Render drop down button with HAR related actions.
-   */
-  renderHarButton() {
-    return button({
-      id: "devtools-har-button",
-      title: TOOLBAR_HAR_BUTTON,
-      className: "devtools-button devtools-har-button",
-      onClick: evt => {
-        this.showHarMenu(evt.target);
-      },
-    });
-  }
-
-  showHarMenu(menuButton) {
-    const {
-      connector,
-      displayedRequests
-    } = this.props;
-
-    let menuItems = [];
-
-    menuItems.push({
-      id: "request-list-context-save-all-as-har",
-      label: L10N.getStr("netmonitor.context.saveAllAsHar"),
-      accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"),
-      disabled: !displayedRequests.length,
-      click: () => HarMenuUtils.saveAllAsHar(displayedRequests, connector),
-    });
-
-    menuItems.push({
-      id: "request-list-context-copy-all-as-har",
-      label: L10N.getStr("netmonitor.context.copyAllAsHar"),
-      accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"),
-      disabled: !displayedRequests.length,
-      click: () => HarMenuUtils.copyAllAsHar(displayedRequests, connector),
-    });
-
-    showMenu(menuItems, { button: menuButton });
-  }
-
-  /**
    * Render filter Searchbox.
    */
   renderFilterBox(setRequestFilterText) {
     return (
       SearchBox({
         delay: FILTER_SEARCH_DELAY,
         keyShortcut: SEARCH_KEY_SHORTCUT,
         placeholder: SEARCH_PLACE_HOLDER,
@@ -342,46 +293,41 @@ class Toolbar extends Component {
           this.renderFilterBox(setRequestFilterText),
           this.renderSeparator(),
           this.renderToggleRecordingButton(recording, toggleRecording),
           this.renderSeparator(),
           this.renderFilterButtons(requestFilterTypes),
           this.renderSeparator(),
           this.renderPersistlogCheckbox(persistentLogsEnabled, togglePersistentLogs),
           this.renderCacheCheckbox(browserCacheDisabled, toggleBrowserCache),
-          this.renderSeparator(),
-          this.renderHarButton(),
         )
       )
     ) : (
       span({ className: "devtools-toolbar devtools-toolbar-container" },
         span({ className: "devtools-toolbar-group devtools-toolbar-two-rows-1" },
           this.renderClearButton(clearRequests),
           this.renderSeparator(),
           this.renderFilterBox(setRequestFilterText),
           this.renderSeparator(),
           this.renderToggleRecordingButton(recording, toggleRecording),
           this.renderSeparator(),
           this.renderPersistlogCheckbox(persistentLogsEnabled, togglePersistentLogs),
           this.renderCacheCheckbox(browserCacheDisabled, toggleBrowserCache),
-          this.renderSeparator(),
-          this.renderHarButton(),
         ),
         span({ className: "devtools-toolbar-group devtools-toolbar-two-rows-2" },
           this.renderFilterButtons(requestFilterTypes)
         )
       )
     );
   }
 }
 
 module.exports = connect(
   (state) => ({
     browserCacheDisabled: state.ui.browserCacheDisabled,
-    displayedRequests: getDisplayedRequests(state),
     filteredRequests: getTypeFilteredRequests(state),
     persistentLogsEnabled: state.ui.persistentLogsEnabled,
     recording: getRecordingState(state),
     requestFilterTypes: state.filters.requestFilterTypes,
   }),
   (dispatch) => ({
     clearRequests: () => dispatch(Actions.clearRequests()),
     disableBrowserCache: (disabled) => dispatch(Actions.disableBrowserCache(disabled)),
deleted file mode 100644
--- a/devtools/client/netmonitor/src/har/har-menu-utils.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/* 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/. */
-
-/* eslint-disable mozilla/reject-some-requires */
-
-"use strict";
-
-loader.lazyRequireGetter(this, "HarExporter", "devtools/client/netmonitor/src/har/har-exporter", true);
-
-/**
- * Helper object with HAR related context menu actions.
- */
-var HarMenuUtils = {
-  /**
-   * Copy HAR from the network panel content to the clipboard.
-   */
-  copyAllAsHar(requests, connector) {
-    return HarExporter.copy(this.getDefaultHarOptions(requests, connector));
-  },
-
-  /**
-   * Save HAR from the network panel content to a file.
-   */
-  saveAllAsHar(requests, connector) {
-    // This will not work in launchpad
-    // document.execCommand(‘cut’/‘copy’) was denied because it was not called from
-    // inside a short running user-generated event handler.
-    // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard
-    return HarExporter.save(this.getDefaultHarOptions(requests, connector));
-  },
-
-  getDefaultHarOptions(requests, connector) {
-    return {
-      connector: connector,
-      items: requests,
-    };
-  },
-};
-
-// Exports from this module
-exports.HarMenuUtils = HarMenuUtils;
--- a/devtools/client/netmonitor/src/har/moz.build
+++ b/devtools/client/netmonitor/src/har/moz.build
@@ -4,14 +4,13 @@
 
 DevToolsModules(
     'har-automation.js',
     'har-builder-utils.js',
     'har-builder.js',
     'har-collector.js',
     'har-exporter.js',
     'har-importer.js',
-    'har-menu-utils.js',
     'har-utils.js',
     'toolbox-overlay.js',
 )
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
--- a/devtools/client/netmonitor/src/utils/menu.js
+++ b/devtools/client/netmonitor/src/utils/menu.js
@@ -2,58 +2,35 @@
  * 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";
 
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
 
-/**
- * Helper function for opening context menu.
- *
- * @param {Array} items List of menu items.
- * @param {Object} options:
- * @property {Number} screenX coordinate of the menu on the screen
- * @property {Number} screenY coordinate of the menu on the screen
- * @property {Object} button parent used to open the menu
- */
-function showMenu(items, options) {
+function showMenu(evt, items) {
   if (items.length === 0) {
     return;
   }
 
-  // Build the menu object from provided menu items.
   let menu = new Menu();
   items.forEach((item) => {
     let menuItem = new MenuItem(item);
     let subItems = item.submenu;
 
     if (subItems) {
       let subMenu = new Menu();
       subItems.forEach((subItem) => {
         subMenu.append(new MenuItem(subItem));
       });
       menuItem.submenu = subMenu;
     }
 
     menu.append(menuItem);
   });
 
-  let screenX = options.screenX;
-  let screenY = options.screenY;
-
-  // Calculate position on the screen according to
-  // the parent button if available.
-  if (options.button) {
-    const button = options.button;
-    const rect = button.getBoundingClientRect();
-    const defaultView = button.ownerDocument.defaultView;
-    screenX = rect.left + defaultView.mozInnerScreenX;
-    screenY = rect.bottom + defaultView.mozInnerScreenY;
-  }
-
-  menu.popup(screenX, screenY, { doc: window.parent.document });
+  menu.popup(evt.screenX, evt.screenY, { doc: window.parent.document });
 }
 
 module.exports = {
   showMenu,
 };
--- a/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js
+++ b/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js
@@ -14,17 +14,17 @@ const {
   parseQueryString,
 } = require("../utils/request-utils");
 
 loader.lazyRequireGetter(this, "Curl", "devtools/client/shared/curl", true);
 loader.lazyRequireGetter(this, "saveAs", "devtools/client/shared/file-saver", true);
 loader.lazyRequireGetter(this, "copyString", "devtools/shared/platform/clipboard", true);
 loader.lazyRequireGetter(this, "showMenu", "devtools/client/netmonitor/src/utils/menu", true);
 loader.lazyRequireGetter(this, "openRequestInTab", "devtools/client/netmonitor/src/utils/firefox/open-request-in-tab", true);
-loader.lazyRequireGetter(this, "HarMenuUtils", "devtools/client/netmonitor/src/har/har-menu-utils", true);
+loader.lazyRequireGetter(this, "HarExporter", "devtools/client/netmonitor/src/har/har-exporter", true);
 
 class RequestListContextMenu {
   constructor(props) {
     this.props = props;
   }
 
   open(event, selectedRequest, requests) {
     let {
@@ -40,17 +40,16 @@ class RequestListContextMenu {
       requestPostDataAvailable,
       responseHeaders,
       responseHeadersAvailable,
       responseContent,
       responseContentAvailable,
       url,
     } = selectedRequest;
     let {
-      connector,
       cloneSelectedRequest,
       openStatistics,
     } = this.props;
     let menu = [];
     let copySubmenu = [];
 
     copySubmenu.push({
       id: "request-list-context-copy-url",
@@ -139,33 +138,33 @@ class RequestListContextMenu {
       type: "separator",
       visible: copySubmenu.slice(5, 9).some((subMenu) => subMenu.visible),
     });
 
     copySubmenu.push({
       id: "request-list-context-copy-all-as-har",
       label: L10N.getStr("netmonitor.context.copyAllAsHar"),
       accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"),
-      visible: requests.length > 0,
-      click: () => HarMenuUtils.copyAllAsHar(requests, connector),
+      visible: requests.size > 0,
+      click: () => this.copyAllAsHar(requests),
     });
 
     menu.push({
       label: L10N.getStr("netmonitor.context.copy"),
       accesskey: L10N.getStr("netmonitor.context.copy.accesskey"),
       visible: !!selectedRequest,
       submenu: copySubmenu,
     });
 
     menu.push({
       id: "request-list-context-save-all-as-har",
       label: L10N.getStr("netmonitor.context.saveAllAsHar"),
       accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"),
-      visible: requests.length > 0,
-      click: () => HarMenuUtils.saveAllAsHar(requests, connector),
+      visible: requests.size > 0,
+      click: () => this.saveAllAsHar(requests),
     });
 
     menu.push({
       id: "request-list-context-save-image-as",
       label: L10N.getStr("netmonitor.context.saveImageAs"),
       accesskey: L10N.getStr("netmonitor.context.saveImageAs.accesskey"),
       visible: !!(selectedRequest && (responseContentAvailable || responseContent) &&
         mimeType && mimeType.includes("image/")),
@@ -219,20 +218,17 @@ class RequestListContextMenu {
     menu.push({
       id: "request-list-context-perf",
       label: L10N.getStr("netmonitor.context.perfTools"),
       accesskey: L10N.getStr("netmonitor.context.perfTools.accesskey"),
       visible: requests.size > 0,
       click: () => openStatistics(true),
     });
 
-    showMenu(menu, {
-      screenX: event.screenX,
-      screenY: event.screenY,
-    });
+    showMenu(event, menu);
   }
 
   /**
    * Opens selected item in a new tab.
    */
   async openRequestInTab(id, url, requestPostData) {
     requestPostData = requestPostData ||
       await this.props.connector.requestData(id, "requestPostData");
@@ -393,11 +389,36 @@ class RequestListContextMenu {
    * Copy response data as a string.
    */
   async copyResponse(id, responseContent) {
     responseContent = responseContent ||
       await this.props.connector.requestData(id, "responseContent");
 
     copyString(responseContent.content.text);
   }
+
+  /**
+   * Copy HAR from the network panel content to the clipboard.
+   */
+  copyAllAsHar(requests) {
+    return HarExporter.copy(this.getDefaultHarOptions(requests));
+  }
+
+  /**
+   * Save HAR from the network panel content to a file.
+   */
+  saveAllAsHar(requests) {
+    // This will not work in launchpad
+    // document.execCommand(‘cut’/‘copy’) was denied because it was not called from
+    // inside a short running user-generated event handler.
+    // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard
+    return HarExporter.save(this.getDefaultHarOptions(requests));
+  }
+
+  getDefaultHarOptions(requests) {
+    return {
+      connector: this.props.connector,
+      items: requests,
+    };
+  }
 }
 
 module.exports = RequestListContextMenu;
--- a/devtools/client/netmonitor/src/widgets/RequestListHeaderContextMenu.js
+++ b/devtools/client/netmonitor/src/widgets/RequestListHeaderContextMenu.js
@@ -65,16 +65,13 @@ class RequestListHeaderContextMenu {
 
     menu.push({ type: "separator" });
     menu.push({
       id: "request-list-header-reset-columns",
       label: L10N.getStr("netmonitor.toolbar.resetColumns"),
       click: () => this.props.resetColumns(),
     });
 
-    showMenu(menu, {
-      screenX: event.screenX,
-      screenY: event.screenY,
-    });
+    return showMenu(event, menu);
   }
 }
 
 module.exports = RequestListHeaderContextMenu;