Bug 1268107 - Cannot use back in about:debugging after entering an invalid hash r?jdescottes draft
authorRicky Chien <ricky060709@gmail.com>
Mon, 09 May 2016 17:14:56 +0800
changeset 367362 482e5d84b11b969a80b847f3eee08f16fac2f9b6
parent 365494 674a552743785c28c75866969aad513bd8eaf6ae
child 520986 30be66d35d2c2521e2f023edbfbbb8531ce54ffb
push id18215
push userbmo:rchien@mozilla.com
push dateMon, 16 May 2016 13:08:48 +0000
reviewersjdescottes
bugs1268107
milestone49.0a1
Bug 1268107 - Cannot use back in about:debugging after entering an invalid hash r?jdescottes MozReview-Commit-ID: CsbzQXiR9RV
devtools/client/aboutdebugging/aboutdebugging.css
devtools/client/aboutdebugging/components/aboutdebugging.js
devtools/client/aboutdebugging/components/panel-menu-entry.js
devtools/client/aboutdebugging/test/.eslintrc
devtools/client/aboutdebugging/test/browser.ini
devtools/client/aboutdebugging/test/browser_page_not_found.js
devtools/client/aboutdebugging/test/head.js
devtools/client/locales/en-US/aboutdebugging.properties
--- a/devtools/client/aboutdebugging/aboutdebugging.css
+++ b/devtools/client/aboutdebugging/aboutdebugging.css
@@ -130,8 +130,16 @@ button {
 .addons-options {
   flex: 1;
 }
 
 .addons-debugging-label {
   display: inline-block;
   margin: 0 5px 5px 0;
 }
+
+.page-not-found {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+}
--- a/devtools/client/aboutdebugging/components/aboutdebugging.js
+++ b/devtools/client/aboutdebugging/components/aboutdebugging.js
@@ -1,14 +1,13 @@
 /* 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-env browser */
-/* globals AddonsPanel, WorkersPanel */
 
 "use strict";
 
 const { createFactory, createClass, DOM: dom } =
   require("devtools/client/shared/vendor/react");
 const Services = require("Services");
 
 const PanelMenu = createFactory(require("./panel-menu"));
@@ -62,41 +61,42 @@ module.exports = createClass({
 
   componentWillUnmount() {
     window.removeEventListener("hashchange", this.onHashChange);
     this.props.telemetry.toolClosed("aboutdebugging");
     this.props.telemetry.destroy();
   },
 
   onHashChange() {
-    let hash = window.location.hash;
-    // Default to defaultTabId if no hash is provided.
-    let panelId = hash ? hash.substr(1) : defaultPanelId;
-
-    let isValid = panels.some(p => p.id == panelId);
-    if (isValid) {
-      this.setState({ selectedPanelId: panelId });
-    } else {
-      // If the current hash matches no valid category, navigate to the default
-      // panel.
-      this.selectPanel(defaultPanelId);
-    }
+    this.setState({
+      selectedPanelId: window.location.hash.substr(1) || defaultPanelId
+    });
   },
 
   selectPanel(panelId) {
     window.location.hash = "#" + panelId;
   },
 
   render() {
     let { client } = this.props;
     let { selectedPanelId } = this.state;
     let selectPanel = this.selectPanel;
+    let selectedPanel = panels.find(p => p.id == selectedPanelId);
+    let panel;
 
-    let selectedPanel = panels.find(p => p.id == selectedPanelId);
+    if (selectedPanel) {
+      panel = selectedPanel.component({ client, id: selectedPanel.id });
+    } else {
+      panel = (
+        dom.div({ className: "page-not-found" },
+          dom.h1({ className: "header-name" },
+            Strings.GetStringFromName("pageNotFound")
+          )
+        )
+      );
+    }
 
     return dom.div({ className: "app" },
       PanelMenu({ panels, selectedPanelId, selectPanel }),
-      dom.div({ className: "main-content" },
-        selectedPanel.component({ client, id: selectedPanel.panelId })
-      )
+      dom.div({ className: "main-content" }, panel)
     );
   }
 });
--- a/devtools/client/aboutdebugging/components/panel-menu-entry.js
+++ b/devtools/client/aboutdebugging/components/panel-menu-entry.js
@@ -9,32 +9,32 @@ const { createClass, DOM: dom } =
 
 module.exports = createClass({
   displayName: "PanelMenuEntry",
 
   onClick() {
     this.props.selectPanel(this.props.id);
   },
 
-  onKeyUp(event) {
+  onKeyDown(event) {
     if ([" ", "Enter"].includes(event.key)) {
       this.props.selectPanel(this.props.id);
     }
   },
 
   render() {
     let { panelId, icon, name, selected } = this.props;
 
     // Here .category, .category-icon, .category-name classnames are used to
     // apply common styles defined.
     let className = "category" + (selected ? " selected" : "");
     return dom.div({
       "aria-selected": selected,
       "aria-controls": panelId,
       className,
       onClick: this.onClick,
-      onKeyUp: this.onKeyUp,
+      onKeyDown: this.onKeyDown,
       tabIndex: "0",
       role: "tab" },
     dom.img({ className: "category-icon", src: icon, role: "presentation" }),
     dom.div({ className: "category-name" }, name));
   }
 });
--- a/devtools/client/aboutdebugging/test/.eslintrc
+++ b/devtools/client/aboutdebugging/test/.eslintrc
@@ -2,21 +2,23 @@
   // Extend from the shared list of defined globals for mochitests.
   "extends": "../../../.eslintrc.mochitests",
   // All globals made available in aboutdebugging head.js file.
   "globals": {
     "AddonManager": true,
     "addTab": true,
     "assertHasTarget": true,
     "CHROME_ROOT": true,
+    "changeAboutDebuggingHash": true,
     "closeAboutDebugging": true,
     "getServiceWorkerList": true,
     "getSupportsFile": true,
     "installAddon": true,
     "openAboutDebugging": true,
+    "openPanel": true,
     "removeTab": true,
     "uninstallAddon": true,
     "unregisterServiceWorker": true,
     "waitForInitialAddonList": true,
     "waitForMutation": true,
     "waitForServiceWorkerRegistered": true
   }
 }
--- a/devtools/client/aboutdebugging/test/browser.ini
+++ b/devtools/client/aboutdebugging/test/browser.ini
@@ -11,15 +11,16 @@ support-files =
   service-workers/push-sw.html
   service-workers/push-sw.js
 
 [browser_addons_debug_bootstrapped.js]
 [browser_addons_debugging_initial_state.js]
 [browser_addons_install.js]
 [browser_addons_reload.js]
 [browser_addons_toggle_debug.js]
+[browser_page_not_found.js]
 [browser_service_workers.js]
 [browser_service_workers_push.js]
 [browser_service_workers_start.js]
 [browser_service_workers_timeout.js]
 skip-if = true # Bug 1232931
 [browser_service_workers_unregister.js]
 [browser_tabs.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/test/browser_page_not_found.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that navigating to a about:debugging#invalid-hash should show up an
+// error page.
+// Every url navigating including #invalid-hash should be kept in history and
+// navigate back as expected.
+add_task(function* () {
+  let { tab, document } = yield openAboutDebugging("invalid-hash");
+  let element = document.querySelector(".header-name");
+  is(element.textContent, "Page not found", "Show error page");
+
+  yield openPanel(document, "addons-panel");
+  yield waitForInitialAddonList(document);
+  element = document.querySelector(".header-name");
+  is(element.textContent, "Add-ons", "Show Addons");
+
+  yield changeAboutDebuggingHash(document, "invalid-hash");
+  element = document.querySelector(".header-name");
+  is(element.textContent, "Page not found", "Show error page");
+
+  gBrowser.goBack();
+  yield waitForMutation(
+    document.querySelector(".main-content"), {childList: true});
+  yield waitForInitialAddonList(document);
+  element = document.querySelector(".header-name");
+  is(element.textContent, "Add-ons", "Show Addons");
+
+  gBrowser.goBack();
+  yield waitForMutation(
+    document.querySelector(".main-content"), {childList: true});
+  element = document.querySelector(".header-name");
+  is(element.textContent, "Page not found", "Show error page");
+
+  yield closeAboutDebugging(tab);
+});
--- a/devtools/client/aboutdebugging/test/head.js
+++ b/devtools/client/aboutdebugging/test/head.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /* eslint-env browser */
 /* eslint-disable mozilla/no-cpows-in-tests */
-/* exported openAboutDebugging, closeAboutDebugging, installAddon,
-   uninstallAddon, waitForMutation, assertHasTarget, getServiceWorkerList,
-   getTabList, waitForInitialAddonList, waitForServiceWorkerRegistered,
-   unregisterServiceWorker */
+/* exported openAboutDebugging, changeAboutDebuggingHash, closeAboutDebugging,
+   installAddon, uninstallAddon, waitForMutation, assertHasTarget,
+   getServiceWorkerList, getTabList, openPanel, waitForInitialAddonList,
+   waitForServiceWorkerRegistered, unregisterServiceWorker */
 /* global sendAsyncMessage */
 
 "use strict";
 
 var { utils: Cu, classes: Cc, interfaces: Ci } = Components;
 
 const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
@@ -38,16 +38,37 @@ function* openAboutDebugging(page) {
 
   if (!document.querySelector(".app")) {
     yield waitForMutation(document.body, { childList: true });
   }
 
   return { tab, document };
 }
 
+/**
+ * Change url hash for current about:debugging tab, return a promise after
+ * new content is loaded.
+ * @param  {DOMDocument}  document   container document from current tab
+ * @param  {String}       hash       hash for about:debugging
+ * @return {Promise}
+ */
+function changeAboutDebuggingHash(document, hash) {
+  info(`Opening about:debugging#${hash}`);
+  window.openUILinkIn(`about:debugging#${hash}`, "current");
+  return waitForMutation(
+    document.querySelector(".main-content"), {childList: true});
+}
+
+function openPanel(document, panelId) {
+  info(`Opening ${panelId} panel`);
+  document.querySelector(`[aria-controls="${panelId}"]`).click();
+  return waitForMutation(
+    document.querySelector(".main-content"), {childList: true});
+}
+
 function closeAboutDebugging(tab) {
   info("Closing about:debugging");
   return removeTab(tab);
 }
 
 function addTab(url, win, backgroundTab = false) {
   info("Adding tab: " + url);
 
--- a/devtools/client/locales/en-US/aboutdebugging.properties
+++ b/devtools/client/locales/en-US/aboutdebugging.properties
@@ -20,10 +20,11 @@ reload = Reload
 
 workers = Workers
 serviceWorkers = Service Workers
 sharedWorkers = Shared Workers
 otherWorkers = Other Workers
 
 tabs = Tabs
 
+pageNotFound = Page not found
+
 nothing = Nothing yet.
-