1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/devtools/tilt/tilt.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,263 @@ 1.4 +/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 +"use strict"; 1.10 + 1.11 +const {Cu} = require("chrome"); 1.12 + 1.13 +let {TiltVisualizer} = require("devtools/tilt/tilt-visualizer"); 1.14 +let TiltGL = require("devtools/tilt/tilt-gl"); 1.15 +let TiltUtils = require("devtools/tilt/tilt-utils"); 1.16 +let EventEmitter = require("devtools/toolkit/event-emitter"); 1.17 +let Telemetry = require("devtools/shared/telemetry"); 1.18 + 1.19 +Cu.import("resource://gre/modules/Services.jsm"); 1.20 + 1.21 +// Tilt notifications dispatched through the nsIObserverService. 1.22 +const TILT_NOTIFICATIONS = { 1.23 + // Called early in the startup of a new tilt instance 1.24 + STARTUP: "tilt-startup", 1.25 + 1.26 + // Fires when Tilt starts the initialization. 1.27 + INITIALIZING: "tilt-initializing", 1.28 + 1.29 + // Fires immediately after initialization is complete. 1.30 + // (when the canvas overlay is visible and the 3D mesh is completely created) 1.31 + INITIALIZED: "tilt-initialized", 1.32 + 1.33 + // Fires immediately before the destruction is started. 1.34 + DESTROYING: "tilt-destroying", 1.35 + 1.36 + // Fires immediately before the destruction is finished. 1.37 + // (just before the canvas overlay is removed from its parent node) 1.38 + BEFORE_DESTROYED: "tilt-before-destroyed", 1.39 + 1.40 + // Fires when Tilt is completely destroyed. 1.41 + DESTROYED: "tilt-destroyed", 1.42 + 1.43 + // Fires when Tilt is shown (after a tab-switch). 1.44 + SHOWN: "tilt-shown", 1.45 + 1.46 + // Fires when Tilt is hidden (after a tab-switch). 1.47 + HIDDEN: "tilt-hidden", 1.48 + 1.49 + // Fires once Tilt highlights an element in the page. 1.50 + HIGHLIGHTING: "tilt-highlighting", 1.51 + 1.52 + // Fires once Tilt stops highlighting any element. 1.53 + UNHIGHLIGHTING: "tilt-unhighlighting", 1.54 + 1.55 + // Fires when a node is removed from the 3D mesh. 1.56 + NODE_REMOVED: "tilt-node-removed" 1.57 +}; 1.58 + 1.59 +let TiltManager = { 1.60 + _instances: new WeakMap(), 1.61 + getTiltForBrowser: function(aChromeWindow) 1.62 + { 1.63 + if (this._instances.has(aChromeWindow)) { 1.64 + return this._instances.get(aChromeWindow); 1.65 + } else { 1.66 + let tilt = new Tilt(aChromeWindow); 1.67 + this._instances.set(aChromeWindow, tilt); 1.68 + return tilt; 1.69 + } 1.70 + }, 1.71 +} 1.72 + 1.73 +exports.TiltManager = TiltManager; 1.74 + 1.75 +/** 1.76 + * Object managing instances of the visualizer. 1.77 + * 1.78 + * @param {Window} aWindow 1.79 + * the chrome window used by each visualizer instance 1.80 + */ 1.81 +function Tilt(aWindow) 1.82 +{ 1.83 + /** 1.84 + * Save a reference to the top-level window. 1.85 + */ 1.86 + this.chromeWindow = aWindow; 1.87 + 1.88 + /** 1.89 + * All the instances of TiltVisualizer. 1.90 + */ 1.91 + this.visualizers = {}; 1.92 + 1.93 + /** 1.94 + * Shortcut for accessing notifications strings. 1.95 + */ 1.96 + this.NOTIFICATIONS = TILT_NOTIFICATIONS; 1.97 + 1.98 + EventEmitter.decorate(this); 1.99 + 1.100 + this.setup(); 1.101 + 1.102 + this._telemetry = new Telemetry(); 1.103 +} 1.104 + 1.105 +Tilt.prototype = { 1.106 + 1.107 + /** 1.108 + * Initializes a visualizer for the current tab or closes it if already open. 1.109 + */ 1.110 + toggle: function T_toggle() 1.111 + { 1.112 + let contentWindow = this.chromeWindow.gBrowser.selectedBrowser.contentWindow; 1.113 + let id = this.currentWindowId; 1.114 + let self = this; 1.115 + 1.116 + contentWindow.addEventListener("beforeunload", function onUnload() { 1.117 + contentWindow.removeEventListener("beforeunload", onUnload, false); 1.118 + self.destroy(id, true); 1.119 + }, false); 1.120 + 1.121 + // if the visualizer for the current tab is already open, destroy it now 1.122 + if (this.visualizers[id]) { 1.123 + this.destroy(id, true); 1.124 + this._telemetry.toolClosed("tilt"); 1.125 + return; 1.126 + } else { 1.127 + this._telemetry.toolOpened("tilt"); 1.128 + } 1.129 + 1.130 + // create a visualizer instance for the current tab 1.131 + this.visualizers[id] = new TiltVisualizer({ 1.132 + chromeWindow: this.chromeWindow, 1.133 + contentWindow: contentWindow, 1.134 + parentNode: this.chromeWindow.gBrowser.selectedBrowser.parentNode, 1.135 + notifications: this.NOTIFICATIONS, 1.136 + tab: this.chromeWindow.gBrowser.selectedTab 1.137 + }); 1.138 + 1.139 + Services.obs.notifyObservers(contentWindow, TILT_NOTIFICATIONS.STARTUP, null); 1.140 + this.visualizers[id].init(); 1.141 + 1.142 + // make sure the visualizer object was initialized properly 1.143 + if (!this.visualizers[id].isInitialized()) { 1.144 + this.destroy(id); 1.145 + this.failureCallback && this.failureCallback(); 1.146 + return; 1.147 + } 1.148 + 1.149 + this.lastInstanceId = id; 1.150 + this.emit("change", this.chromeWindow.gBrowser.selectedTab); 1.151 + Services.obs.notifyObservers(contentWindow, TILT_NOTIFICATIONS.INITIALIZING, null); 1.152 + }, 1.153 + 1.154 + /** 1.155 + * Starts destroying a specific instance of the visualizer. 1.156 + * 1.157 + * @param {String} aId 1.158 + * the identifier of the instance in the visualizers array 1.159 + * @param {Boolean} aAnimateFlag 1.160 + * optional, set to true to display a destruction transition 1.161 + */ 1.162 + destroy: function T_destroy(aId, aAnimateFlag) 1.163 + { 1.164 + // if the visualizer is destroyed or destroying, don't do anything 1.165 + if (!this.visualizers[aId] || this._isDestroying) { 1.166 + return; 1.167 + } 1.168 + this._isDestroying = true; 1.169 + 1.170 + let controller = this.visualizers[aId].controller; 1.171 + let presenter = this.visualizers[aId].presenter; 1.172 + 1.173 + let content = presenter.contentWindow; 1.174 + let pageXOffset = content.pageXOffset * presenter.transforms.zoom; 1.175 + let pageYOffset = content.pageYOffset * presenter.transforms.zoom; 1.176 + TiltUtils.setDocumentZoom(this.chromeWindow, presenter.transforms.zoom); 1.177 + 1.178 + // if we're not doing any outro animation, just finish destruction directly 1.179 + if (!aAnimateFlag) { 1.180 + this._finish(aId); 1.181 + return; 1.182 + } 1.183 + 1.184 + // otherwise, trigger the outro animation and notify necessary observers 1.185 + Services.obs.notifyObservers(content, TILT_NOTIFICATIONS.DESTROYING, null); 1.186 + 1.187 + controller.removeEventListeners(); 1.188 + controller.arcball.reset([-pageXOffset, -pageYOffset]); 1.189 + presenter.executeDestruction(this._finish.bind(this, aId)); 1.190 + }, 1.191 + 1.192 + /** 1.193 + * Finishes detroying a specific instance of the visualizer. 1.194 + * 1.195 + * @param {String} aId 1.196 + * the identifier of the instance in the visualizers array 1.197 + */ 1.198 + _finish: function T__finish(aId) 1.199 + { 1.200 + let contentWindow = this.visualizers[aId].presenter.contentWindow; 1.201 + this.visualizers[aId].removeOverlay(); 1.202 + this.visualizers[aId].cleanup(); 1.203 + this.visualizers[aId] = null; 1.204 + 1.205 + this._isDestroying = false; 1.206 + this.chromeWindow.gBrowser.selectedBrowser.focus(); 1.207 + this.emit("change", this.chromeWindow.gBrowser.selectedTab); 1.208 + Services.obs.notifyObservers(contentWindow, TILT_NOTIFICATIONS.DESTROYED, null); 1.209 + }, 1.210 + 1.211 + /** 1.212 + * Handles the event fired when a tab is selected. 1.213 + */ 1.214 + _onTabSelect: function T__onTabSelect() 1.215 + { 1.216 + if (this.visualizers[this.lastInstanceId]) { 1.217 + let contentWindow = this.visualizers[this.lastInstanceId].presenter.contentWindow; 1.218 + Services.obs.notifyObservers(contentWindow, TILT_NOTIFICATIONS.HIDDEN, null); 1.219 + } 1.220 + 1.221 + if (this.currentInstance) { 1.222 + let contentWindow = this.currentInstance.presenter.contentWindow; 1.223 + Services.obs.notifyObservers(contentWindow, TILT_NOTIFICATIONS.SHOWN, null); 1.224 + } 1.225 + 1.226 + this.lastInstanceId = this.currentWindowId; 1.227 + }, 1.228 + 1.229 + /** 1.230 + * Add the browser event listeners to handle state changes. 1.231 + */ 1.232 + setup: function T_setup() 1.233 + { 1.234 + // load the preferences from the devtools.tilt branch 1.235 + TiltVisualizer.Prefs.load(); 1.236 + 1.237 + this.chromeWindow.gBrowser.tabContainer.addEventListener( 1.238 + "TabSelect", this._onTabSelect.bind(this), false); 1.239 + }, 1.240 + 1.241 + /** 1.242 + * Returns true if this tool is enabled. 1.243 + */ 1.244 + get enabled() 1.245 + { 1.246 + return (TiltVisualizer.Prefs.enabled && 1.247 + (TiltGL.isWebGLForceEnabled() || TiltGL.isWebGLSupported())); 1.248 + }, 1.249 + 1.250 + /** 1.251 + * Gets the ID of the current window object to identify the visualizer. 1.252 + */ 1.253 + get currentWindowId() 1.254 + { 1.255 + return TiltUtils.getWindowId( 1.256 + this.chromeWindow.gBrowser.selectedBrowser.contentWindow); 1.257 + }, 1.258 + 1.259 + /** 1.260 + * Gets the visualizer instance for the current tab. 1.261 + */ 1.262 + get currentInstance() 1.263 + { 1.264 + return this.visualizers[this.currentWindowId]; 1.265 + }, 1.266 +};