Bug 1268107 - Cannot use back in about:debugging after entering an invalid hash r?jdescottes
MozReview-Commit-ID: CsbzQXiR9RV
--- 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.
-