Bug 1239437 - Add a basic viewport frame. r=pbrosset
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -140,16 +140,18 @@ devtools.jar:
content/shared/widgets/filter-frame.xhtml (shared/widgets/filter-frame.xhtml)
content/shared/widgets/filter-widget.css (shared/widgets/filter-widget.css)
content/eyedropper/eyedropper.xul (eyedropper/eyedropper.xul)
content/eyedropper/crosshairs.css (eyedropper/crosshairs.css)
content/eyedropper/nocursor.css (eyedropper/nocursor.css)
content/aboutdebugging/aboutdebugging.xhtml (aboutdebugging/aboutdebugging.xhtml)
content/aboutdebugging/aboutdebugging.css (aboutdebugging/aboutdebugging.css)
content/aboutdebugging/aboutdebugging.js (aboutdebugging/aboutdebugging.js)
+ content/responsive.html/index.xhtml (responsive.html/index.xhtml)
+ content/responsive.html/index.js (responsive.html/index.js)
% skin devtools classic/1.0 %skin/
skin/devtools-browser.css (themes/devtools-browser.css)
skin/common.css (themes/common.css)
skin/splitters.css (themes/splitters.css)
skin/dark-theme.css (themes/dark-theme.css)
skin/light-theme.css (themes/light-theme.css)
skin/toolbars.css (themes/toolbars.css)
skin/variables.css (themes/variables.css)
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/index.js
@@ -0,0 +1,17 @@
+/* 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 */
+
+"use strict";
+
+window.addViewport = contentURI => {
+ try {
+ let frame = document.createElement("iframe");
+ frame.setAttribute("src", contentURI);
+ document.body.appendChild(frame);
+ } catch (e) {
+ console.error(e);
+ }
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/index.xhtml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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/. -->
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://devtools/skin/common.css"/>
+ <script type="application/javascript;version=1.8"
+ src="chrome://devtools/content/shared/theme-switching.js"></script>
+ <script type="application/javascript;version=1.8"
+ src="./index.js"></script>
+ </head>
+ <body class="theme-body" role="application">
+ </body>
+</html>
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -1,17 +1,20 @@
/* 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/. */
"use strict";
const promise = require("promise");
+const { Task } = require("resource://gre/modules/Task.jsm");
const EventEmitter = require("devtools/shared/event-emitter");
+const TOOL_URL = "chrome://devtools/content/responsive.html/index.xhtml";
+
/**
* ResponsiveUIManager is the external API for the browser UI, etc. to use when
* opening and closing the responsive UI.
*
* While the HTML UI is in an experimental stage, the older ResponsiveUIManager
* from devtools/client/responsivedesign/responsivedesign.jsm delegates to this
* object when the pref "devtools.responsive.html.enabled" is true.
*/
@@ -25,17 +28,18 @@ exports.ResponsiveUIManager = {
* The main browser chrome window.
* @param tab
* The browser tab.
* @return Promise
* Resolved when the toggling has completed.
*/
toggle(window, tab) {
if (this.isActiveForTab(tab)) {
- this.activeTabs.get(tab).close();
+ this.activeTabs.get(tab).destroy();
+ this.activeTabs.delete(tab);
} else {
this.runIfNeeded(window, tab);
}
// TODO: Becomes a more interesting value in a later patch
return promise.resolve();
},
/**
@@ -43,17 +47,17 @@ exports.ResponsiveUIManager = {
*
* @param window
* The main browser chrome window.
* @param tab
* The browser tab.
*/
runIfNeeded(window, tab) {
if (!this.isActiveForTab(tab)) {
- // TODO: Unimplemented
+ this.activeTabs.set(tab, new ResponsiveUI(window, tab));
}
},
/**
* Returns true if responsive UI is active for a given tab.
*
* @param tab
* The browser tab.
@@ -63,17 +67,18 @@ exports.ResponsiveUIManager = {
return this.activeTabs.has(tab);
},
/**
* Return the responsive UI controller for a tab.
*
* @param tab
* The browser tab.
- * @return TODO: Some object!
+ * @return ResponsiveUI
+ * The UI instance for this tab.
*/
getResponsiveUIForTab(tab) {
return this.activeTabs.get(tab);
},
/**
* Handle GCLI commands.
*
@@ -93,23 +98,94 @@ exports.ResponsiveUIManager = {
// TODO: Probably the wrong API
this.activeTabs.get(tab).setSize(args.width, args.height);
break;
case "resize on":
this.runIfNeeded(window, tab);
break;
case "resize off":
if (this.isActiveForTab(tab)) {
- // TODO: Probably the wrong API
- this.activeTabs.get(tab).close();
+ this.activeTabs.get(tab).destroy();
+ this.activeTabs.delete(tab);
}
break;
case "resize toggle":
this.toggle(window, tab);
break;
default:
}
}
};
// GCLI commands in ../responsivedesign/resize-commands.js listen for events
// from this object to know when the UI for a tab has opened or closed.
EventEmitter.decorate(exports.ResponsiveUIManager);
+
+/**
+ * ResponsiveUI manages the responsive design tool for a specific tab. The
+ * actual tool itself lives in a separate chrome:// document that is loaded into
+ * the tab upon opening responsive design. This object acts a helper to
+ * integrate the tool into the surrounding browser UI as needed.
+ */
+function ResponsiveUI(window, tab) {
+ this.window = window;
+ this.tab = tab;
+ this.init();
+}
+
+ResponsiveUI.prototype = {
+
+ /**
+ * The main browser chrome window (that holds many tabs).
+ */
+ window: null,
+
+ /**
+ * The specific browser tab this responsive instance is for.
+ */
+ tab: null,
+
+ /**
+ * For the moment, we open the tool by:
+ * 1. Recording the tab's URL
+ * 2. Navigating the tab to the tool
+ * 3. Passing along the URL to the tool to open in the viewport
+ *
+ * This approach is simple, but it also discards the user's state on the page.
+ * It's just like opening a fresh tab and pasting the URL.
+ *
+ * In the future, we can do better by using swapFrameLoaders to preserve the
+ * state. Platform discussions are in progress to make this happen. See
+ * bug 1238160 about <iframe mozbrowser> for more details.
+ */
+ init: Task.async(function*() {
+ let tabBrowser = this.tab.linkedBrowser;
+ let contentURI = tabBrowser.documentURI.spec;
+ tabBrowser.loadURI(TOOL_URL);
+ yield tabLoaded(this.tab);
+ let toolWindow = tabBrowser.contentWindow;
+ toolWindow.addViewport(contentURI);
+ }),
+
+ destroy() {
+ let tabBrowser = this.tab.linkedBrowser;
+ tabBrowser.goBack();
+ this.window = null;
+ this.tab = null;
+ },
+
+};
+
+function tabLoaded(tab) {
+ let deferred = promise.defer();
+
+ function handle(event) {
+ if (event.originalTarget != tab.linkedBrowser.contentDocument ||
+ event.target.location.href == "about:blank") {
+ return;
+ }
+ tab.linkedBrowser.removeEventListener("load", handle, true);
+ deferred.resolve(event);
+ }
+
+ tab.linkedBrowser.addEventListener("load", handle, true);
+ return deferred.promise;
+}