/* 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");