diff options
Diffstat (limited to 'devtools/client/responsive.html/components/browser.js')
-rw-r--r-- | devtools/client/responsive.html/components/browser.js | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/devtools/client/responsive.html/components/browser.js b/devtools/client/responsive.html/components/browser.js new file mode 100644 index 000000000..f2902905b --- /dev/null +++ b/devtools/client/responsive.html/components/browser.js @@ -0,0 +1,149 @@ +/* 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"; + +const { Task } = require("devtools/shared/task"); +const flags = require("devtools/shared/flags"); +const { getToplevelWindow } = require("sdk/window/utils"); +const { DOM: dom, createClass, addons, PropTypes } = + require("devtools/client/shared/vendor/react"); + +const Types = require("../types"); +const e10s = require("../utils/e10s"); +const message = require("../utils/message"); + +module.exports = createClass({ + + /** + * This component is not allowed to depend directly on frequently changing + * data (width, height) due to the use of `dangerouslySetInnerHTML` below. + * Any changes in props will cause the <iframe> to be removed and added again, + * throwing away the current state of the page. + */ + displayName: "Browser", + + propTypes: { + location: Types.location.isRequired, + swapAfterMount: PropTypes.bool.isRequired, + onBrowserMounted: PropTypes.func.isRequired, + onContentResize: PropTypes.func.isRequired, + }, + + mixins: [ addons.PureRenderMixin ], + + /** + * Once the browser element has mounted, load the frame script and enable + * various features, like floating scrollbars. + */ + componentDidMount: Task.async(function* () { + // If we are not swapping browsers after mount, it's safe to start the frame + // script now. + if (!this.props.swapAfterMount) { + yield this.startFrameScript(); + } + + // Notify manager.js that this browser has mounted, so that it can trigger + // a swap if needed and continue with the rest of its startup. + this.props.onBrowserMounted(); + + // If we are swapping browsers after mount, wait for the swap to complete + // and start the frame script after that. + if (this.props.swapAfterMount) { + yield message.wait(window, "start-frame-script"); + yield this.startFrameScript(); + message.post(window, "start-frame-script:done"); + } + + // Stop the frame script when requested in the future. + message.wait(window, "stop-frame-script").then(() => { + this.stopFrameScript(); + }); + }), + + onContentResize(msg) { + let { onContentResize } = this.props; + let { width, height } = msg.data; + onContentResize({ + width, + height, + }); + }, + + startFrameScript: Task.async(function* () { + let { onContentResize } = this; + let browser = this.refs.browserContainer.querySelector("iframe.browser"); + let mm = browser.frameLoader.messageManager; + + // Notify tests when the content has received a resize event. This is not + // quite the same timing as when we _set_ a new size around the browser, + // since it still needs to do async work before the content is actually + // resized to match. + e10s.on(mm, "OnContentResize", onContentResize); + + let ready = e10s.once(mm, "ChildScriptReady"); + mm.loadFrameScript("resource://devtools/client/responsivedesign/" + + "responsivedesign-child.js", true); + yield ready; + + let browserWindow = getToplevelWindow(window); + let requiresFloatingScrollbars = + !browserWindow.matchMedia("(-moz-overlay-scrollbars)").matches; + + yield e10s.request(mm, "Start", { + requiresFloatingScrollbars, + // Tests expect events on resize to yield on various size changes + notifyOnResize: flags.testing, + }); + }), + + stopFrameScript: Task.async(function* () { + let { onContentResize } = this; + + let browser = this.refs.browserContainer.querySelector("iframe.browser"); + let mm = browser.frameLoader.messageManager; + e10s.off(mm, "OnContentResize", onContentResize); + yield e10s.request(mm, "Stop"); + message.post(window, "stop-frame-script:done"); + }), + + render() { + let { + location, + } = this.props; + + // innerHTML expects & to be an HTML entity + location = location.replace(/&/g, "&"); + + return dom.div( + { + ref: "browserContainer", + className: "browser-container", + + /** + * React uses a whitelist for attributes, so we need some way to set + * attributes it does not know about, such as @mozbrowser. If this were + * the only issue, we could use componentDidMount or ref: node => {} to + * set the atttibutes. In the case of @remote, the attribute must be set + * before the element is added to the DOM to have any effect, which we + * are able to do with this approach. + * + * @noisolation and @allowfullscreen are needed so that these frames + * have the same access to browser features as regular browser tabs. + * The `swapFrameLoaders` platform API we use compares such features + * before allowing the swap to proceed. + */ + dangerouslySetInnerHTML: { + __html: `<iframe class="browser" mozbrowser="true" remote="true" + noisolation="true" allowfullscreen="true" + src="${location}" width="100%" height="100%"> + </iframe>` + } + } + ); + }, + +}); |