diff options
Diffstat (limited to 'devtools/client/responsivedesign/responsivedesign-child.js')
-rw-r--r-- | devtools/client/responsivedesign/responsivedesign-child.js | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/devtools/client/responsivedesign/responsivedesign-child.js b/devtools/client/responsivedesign/responsivedesign-child.js new file mode 100644 index 000000000..a6ce091e2 --- /dev/null +++ b/devtools/client/responsivedesign/responsivedesign-child.js @@ -0,0 +1,195 @@ +/* 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"; + +/* global content, docShell, addEventListener, addMessageListener, + removeEventListener, removeMessageListener, sendAsyncMessage, Services */ + +var global = this; + +// Guard against loading this frame script mutiple times +(function () { + if (global.responsiveFrameScriptLoaded) { + return; + } + + var Ci = Components.interfaces; + const gDeviceSizeWasPageSize = docShell.deviceSizeIsPageSize; + const gFloatingScrollbarsStylesheet = Services.io.newURI("chrome://devtools/skin/floating-scrollbars-responsive-design.css", null, null); + var gRequiresFloatingScrollbars; + + var active = false; + var resizeNotifications = false; + + addMessageListener("ResponsiveMode:Start", startResponsiveMode); + addMessageListener("ResponsiveMode:Stop", stopResponsiveMode); + addMessageListener("ResponsiveMode:IsActive", isActive); + + function debug(msg) { + // dump(`RDM CHILD: ${msg}\n`); + } + + /** + * Used by tests to verify the state of responsive mode. + */ + function isActive() { + sendAsyncMessage("ResponsiveMode:IsActive:Done", { active }); + } + + function startResponsiveMode({data:data}) { + debug("START"); + if (active) { + debug("ALREADY STARTED"); + sendAsyncMessage("ResponsiveMode:Start:Done"); + return; + } + addMessageListener("ResponsiveMode:RequestScreenshot", screenshot); + let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress); + webProgress.addProgressListener(WebProgressListener, Ci.nsIWebProgress.NOTIFY_ALL); + docShell.deviceSizeIsPageSize = true; + gRequiresFloatingScrollbars = data.requiresFloatingScrollbars; + if (data.notifyOnResize) { + startOnResize(); + } + + // At this point, a content viewer might not be loaded for this + // docshell. makeScrollbarsFloating will be triggered by onLocationChange. + if (docShell.contentViewer) { + makeScrollbarsFloating(); + } + active = true; + sendAsyncMessage("ResponsiveMode:Start:Done"); + } + + function onResize() { + let { width, height } = content.screen; + debug(`EMIT RESIZE: ${width} x ${height}`); + sendAsyncMessage("ResponsiveMode:OnContentResize", { + width, + height, + }); + } + + function bindOnResize() { + content.addEventListener("resize", onResize, false); + } + + function startOnResize() { + debug("START ON RESIZE"); + if (resizeNotifications) { + return; + } + resizeNotifications = true; + bindOnResize(); + addEventListener("DOMWindowCreated", bindOnResize, false); + } + + function stopOnResize() { + debug("STOP ON RESIZE"); + if (!resizeNotifications) { + return; + } + resizeNotifications = false; + content.removeEventListener("resize", onResize, false); + removeEventListener("DOMWindowCreated", bindOnResize, false); + } + + function stopResponsiveMode() { + debug("STOP"); + if (!active) { + debug("ALREADY STOPPED, ABORT"); + return; + } + active = false; + removeMessageListener("ResponsiveMode:RequestScreenshot", screenshot); + let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress); + webProgress.removeProgressListener(WebProgressListener); + docShell.deviceSizeIsPageSize = gDeviceSizeWasPageSize; + restoreScrollbars(); + stopOnResize(); + sendAsyncMessage("ResponsiveMode:Stop:Done"); + } + + function makeScrollbarsFloating() { + if (!gRequiresFloatingScrollbars) { + return; + } + + let allDocShells = [docShell]; + + for (let i = 0; i < docShell.childCount; i++) { + let child = docShell.getChildAt(i).QueryInterface(Ci.nsIDocShell); + allDocShells.push(child); + } + + for (let d of allDocShells) { + let win = d.contentViewer.DOMDocument.defaultView; + let winUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + try { + winUtils.loadSheet(gFloatingScrollbarsStylesheet, win.AGENT_SHEET); + } catch (e) { } + } + + flushStyle(); + } + + function restoreScrollbars() { + let allDocShells = [docShell]; + for (let i = 0; i < docShell.childCount; i++) { + allDocShells.push(docShell.getChildAt(i).QueryInterface(Ci.nsIDocShell)); + } + for (let d of allDocShells) { + let win = d.contentViewer.DOMDocument.defaultView; + let winUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + try { + winUtils.removeSheet(gFloatingScrollbarsStylesheet, win.AGENT_SHEET); + } catch (e) { } + } + flushStyle(); + } + + function flushStyle() { + // Force presContext destruction + let isSticky = docShell.contentViewer.sticky; + docShell.contentViewer.sticky = false; + docShell.contentViewer.hide(); + docShell.contentViewer.show(); + docShell.contentViewer.sticky = isSticky; + } + + function screenshot() { + let canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); + let ratio = content.devicePixelRatio; + let width = content.innerWidth * ratio; + let height = content.innerHeight * ratio; + canvas.mozOpaque = true; + canvas.width = width; + canvas.height = height; + let ctx = canvas.getContext("2d"); + ctx.scale(ratio, ratio); + ctx.drawWindow(content, content.scrollX, content.scrollY, width, height, "#fff"); + sendAsyncMessage("ResponsiveMode:RequestScreenshot:Done", canvas.toDataURL()); + } + + var WebProgressListener = { + onLocationChange(webProgress, request, URI, flags) { + if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) { + return; + } + makeScrollbarsFloating(); + }, + QueryInterface: function QueryInterface(aIID) { + if (aIID.equals(Ci.nsIWebProgressListener) || + aIID.equals(Ci.nsISupportsWeakReference) || + aIID.equals(Ci.nsISupports)) { + return this; + } + throw Components.results.NS_ERROR_NO_INTERFACE; + } + }; +})(); + +global.responsiveFrameScriptLoaded = true; +sendAsyncMessage("ResponsiveMode:ChildScriptReady"); |