1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/metro/base/content/browser.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1646 @@ 1.4 +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +let Cc = Components.classes; 1.10 +let Ci = Components.interfaces; 1.11 +let Cu = Components.utils; 1.12 +let Cr = Components.results; 1.13 + 1.14 +Cu.import("resource://gre/modules/PageThumbs.jsm"); 1.15 + 1.16 +// Page for which the start UI is shown 1.17 +const kStartURI = "about:newtab"; 1.18 + 1.19 +// allow panning after this timeout on pages with registered touch listeners 1.20 +const kTouchTimeout = 300; 1.21 +const kSetInactiveStateTimeout = 100; 1.22 + 1.23 +const kTabThumbnailDelayCapture = 500; 1.24 + 1.25 +const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 1.26 + 1.27 +// See grid.xml, we use this to cache style info across loads of the startui. 1.28 +var _richgridTileSizes = {}; 1.29 + 1.30 +// Override sizeToContent in the main window. It breaks things (bug 565887) 1.31 +window.sizeToContent = function() { 1.32 + Cu.reportError("window.sizeToContent is not allowed in this window"); 1.33 +} 1.34 + 1.35 +function getTabModalPromptBox(aWindow) { 1.36 + let browser = Browser.getBrowserForWindow(aWindow); 1.37 + return Browser.getTabModalPromptBox(browser); 1.38 +} 1.39 + 1.40 +/* 1.41 + * Returns the browser for the currently displayed tab. 1.42 + */ 1.43 +function getBrowser() { 1.44 + return Browser.selectedBrowser; 1.45 +} 1.46 + 1.47 +var Browser = { 1.48 + _debugEvents: false, 1.49 + _tabs: [], 1.50 + _selectedTab: null, 1.51 + _tabId: 0, 1.52 + windowUtils: window.QueryInterface(Ci.nsIInterfaceRequestor) 1.53 + .getInterface(Ci.nsIDOMWindowUtils), 1.54 + 1.55 + get defaultBrowserWidth() { 1.56 + return window.innerWidth; 1.57 + }, 1.58 + 1.59 + startup: function startup() { 1.60 + var self = this; 1.61 + 1.62 + try { 1.63 + messageManager.loadFrameScript("chrome://browser/content/Util.js", true); 1.64 + messageManager.loadFrameScript("chrome://browser/content/contenthandlers/Content.js", true); 1.65 + messageManager.loadFrameScript("chrome://browser/content/contenthandlers/FormHelper.js", true); 1.66 + messageManager.loadFrameScript("chrome://browser/content/library/SelectionPrototype.js", true); 1.67 + messageManager.loadFrameScript("chrome://browser/content/contenthandlers/SelectionHandler.js", true); 1.68 + messageManager.loadFrameScript("chrome://browser/content/contenthandlers/ContextMenuHandler.js", true); 1.69 + messageManager.loadFrameScript("chrome://browser/content/contenthandlers/ConsoleAPIObserver.js", true); 1.70 + messageManager.loadFrameScript("chrome://browser/content/contenthandlers/PluginHelper.js", true); 1.71 + } catch (e) { 1.72 + // XXX whatever is calling startup needs to dump errors! 1.73 + dump("###########" + e + "\n"); 1.74 + } 1.75 + 1.76 + if (!Services.metro) { 1.77 + // Services.metro is only available on Windows Metro. We want to be able 1.78 + // to test metro on other platforms, too, so we provide a minimal shim. 1.79 + Services.metro = { 1.80 + activationURI: "", 1.81 + pinTileAsync: function () {}, 1.82 + unpinTileAsync: function () {} 1.83 + }; 1.84 + } 1.85 + 1.86 + /* handles dispatching clicks on browser into clicks in content or zooms */ 1.87 + Elements.browsers.customDragger = new Browser.MainDragger(); 1.88 + 1.89 + /* handles web progress management for open browsers */ 1.90 + Elements.browsers.webProgress = WebProgress.init(); 1.91 + 1.92 + // Call InputSourceHelper first so global listeners get called before 1.93 + // we start processing input in TouchModule. 1.94 + InputSourceHelper.init(); 1.95 + ClickEventHandler.init(); 1.96 + 1.97 + TouchModule.init(); 1.98 + GestureModule.init(); 1.99 + BrowserTouchHandler.init(); 1.100 + PopupBlockerObserver.init(); 1.101 + APZCObserver.init(); 1.102 + 1.103 + // Init the touch scrollbox 1.104 + this.contentScrollbox = Elements.browsers; 1.105 + this.contentScrollboxScroller = { 1.106 + scrollBy: function(aDx, aDy) { 1.107 + let view = getBrowser().getRootView(); 1.108 + view.scrollBy(aDx, aDy); 1.109 + }, 1.110 + 1.111 + scrollTo: function(aX, aY) { 1.112 + let view = getBrowser().getRootView(); 1.113 + view.scrollTo(aX, aY); 1.114 + }, 1.115 + 1.116 + getPosition: function(aScrollX, aScrollY) { 1.117 + let view = getBrowser().getRootView(); 1.118 + let scroll = view.getPosition(); 1.119 + aScrollX.value = scroll.x; 1.120 + aScrollY.value = scroll.y; 1.121 + } 1.122 + }; 1.123 + 1.124 + ContentAreaObserver.init(); 1.125 + 1.126 + function fullscreenHandler() { 1.127 + if (Browser.selectedBrowser.contentWindow.document.mozFullScreenElement) 1.128 + Elements.stack.setAttribute("fullscreen", "true"); 1.129 + else 1.130 + Elements.stack.removeAttribute("fullscreen"); 1.131 + } 1.132 + window.addEventListener("mozfullscreenchange", fullscreenHandler, true); 1.133 + 1.134 + BrowserUI.init(); 1.135 + 1.136 + window.controllers.appendController(this); 1.137 + window.controllers.appendController(BrowserUI); 1.138 + 1.139 + Services.obs.addObserver(SessionHistoryObserver, "browser:purge-session-history", false); 1.140 + 1.141 + window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess(); 1.142 + 1.143 + Elements.browsers.addEventListener("DOMUpdatePageReport", PopupBlockerObserver.onUpdatePageReport, false); 1.144 + 1.145 + // Make sure we're online before attempting to load 1.146 + Util.forceOnline(); 1.147 + 1.148 + // If this is an intial window launch the commandline handler passes us the default 1.149 + // page as an argument. 1.150 + let commandURL = null; 1.151 + try { 1.152 + let argsObj = window.arguments[0].wrappedJSObject; 1.153 + if (argsObj && argsObj.pageloadURL) { 1.154 + // Talos tp-cmdline parameter 1.155 + commandURL = argsObj.pageloadURL; 1.156 + } else if (window.arguments && window.arguments[0]) { 1.157 + // BrowserCLH paramerter 1.158 + commandURL = window.arguments[0]; 1.159 + } 1.160 + } catch (ex) { 1.161 + Util.dumpLn(ex); 1.162 + } 1.163 + 1.164 + messageManager.addMessageListener("DOMLinkAdded", this); 1.165 + messageManager.addMessageListener("Browser:FormSubmit", this); 1.166 + messageManager.addMessageListener("Browser:CanUnload:Return", this); 1.167 + messageManager.addMessageListener("scroll", this); 1.168 + messageManager.addMessageListener("Browser:CertException", this); 1.169 + messageManager.addMessageListener("Browser:BlockedSite", this); 1.170 + 1.171 + Task.spawn(function() { 1.172 + // Activation URIs come from protocol activations, secondary tiles, and file activations 1.173 + let activationURI = yield this.getShortcutOrURI(Services.metro.activationURI); 1.174 + 1.175 + let self = this; 1.176 + function loadStartupURI() { 1.177 + if (activationURI) { 1.178 + let webNav = Ci.nsIWebNavigation; 1.179 + let flags = webNav.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | 1.180 + webNav.LOAD_FLAGS_FIXUP_SCHEME_TYPOS; 1.181 + self.addTab(activationURI, true, null, { flags: flags }); 1.182 + } else { 1.183 + let uri = commandURL || Browser.getHomePage(); 1.184 + self.addTab(uri, true); 1.185 + } 1.186 + } 1.187 + 1.188 + // Should we restore the previous session (crash or some other event) 1.189 + let ss = Cc["@mozilla.org/browser/sessionstore;1"] 1.190 + .getService(Ci.nsISessionStore); 1.191 + let shouldRestore = ss.shouldRestore(); 1.192 + if (shouldRestore) { 1.193 + let bringFront = false; 1.194 + // First open any commandline URLs, except the homepage 1.195 + if (activationURI && activationURI != kStartURI) { 1.196 + this.addTab(activationURI, true, null, { flags: Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP }); 1.197 + } else if (commandURL && commandURL != kStartURI) { 1.198 + this.addTab(commandURL, true); 1.199 + } else { 1.200 + bringFront = true; 1.201 + // Initial window resizes call functions that assume a tab is in the tab list 1.202 + // and restored tabs are added too late. We add a dummy to to satisfy the resize 1.203 + // code and then remove the dummy after the session has been restored. 1.204 + let dummy = this.addTab("about:blank", true); 1.205 + let dummyCleanup = { 1.206 + observe: function(aSubject, aTopic, aData) { 1.207 + Services.obs.removeObserver(dummyCleanup, "sessionstore-windows-restored"); 1.208 + if (aData == "fail") 1.209 + loadStartupURI(); 1.210 + dummy.chromeTab.ignoreUndo = true; 1.211 + Browser.closeTab(dummy, { forceClose: true }); 1.212 + } 1.213 + }; 1.214 + Services.obs.addObserver(dummyCleanup, "sessionstore-windows-restored", false); 1.215 + } 1.216 + ss.restoreLastSession(bringFront); 1.217 + } else { 1.218 + loadStartupURI(); 1.219 + } 1.220 + 1.221 + // Notify about our input type 1.222 + InputSourceHelper.fireUpdate(); 1.223 + 1.224 + // Broadcast a UIReady message so add-ons know we are finished with startup 1.225 + let event = document.createEvent("Events"); 1.226 + event.initEvent("UIReady", true, false); 1.227 + window.dispatchEvent(event); 1.228 + }.bind(this)); 1.229 + }, 1.230 + 1.231 + shutdown: function shutdown() { 1.232 + APZCObserver.shutdown(); 1.233 + BrowserUI.uninit(); 1.234 + ClickEventHandler.uninit(); 1.235 + ContentAreaObserver.shutdown(); 1.236 + Appbar.shutdown(); 1.237 + 1.238 + messageManager.removeMessageListener("Browser:FormSubmit", this); 1.239 + messageManager.removeMessageListener("scroll", this); 1.240 + messageManager.removeMessageListener("Browser:CertException", this); 1.241 + messageManager.removeMessageListener("Browser:BlockedSite", this); 1.242 + 1.243 + Services.obs.removeObserver(SessionHistoryObserver, "browser:purge-session-history"); 1.244 + 1.245 + window.controllers.removeController(this); 1.246 + window.controllers.removeController(BrowserUI); 1.247 + }, 1.248 + 1.249 + getHomePage: function getHomePage(aOptions) { 1.250 + aOptions = aOptions || { useDefault: false }; 1.251 + 1.252 + let url = kStartURI; 1.253 + try { 1.254 + let prefs = aOptions.useDefault ? Services.prefs.getDefaultBranch(null) : Services.prefs; 1.255 + url = prefs.getComplexValue("browser.startup.homepage", Ci.nsIPrefLocalizedString).data; 1.256 + } 1.257 + catch(e) { } 1.258 + 1.259 + return url; 1.260 + }, 1.261 + 1.262 + get browsers() { 1.263 + return this._tabs.map(function(tab) { return tab.browser; }); 1.264 + }, 1.265 + 1.266 + /** 1.267 + * Load a URI in the current tab, or a new tab if necessary. 1.268 + * @param aURI String 1.269 + * @param aParams Object with optional properties that will be passed to loadURIWithFlags: 1.270 + * flags, referrerURI, charset, postData. 1.271 + */ 1.272 + loadURI: function loadURI(aURI, aParams) { 1.273 + let browser = this.selectedBrowser; 1.274 + 1.275 + // We need to keep about: pages opening in new "local" tabs. We also want to spawn 1.276 + // new "remote" tabs if opening web pages from a "local" about: page. 1.277 + dump("loadURI=" + aURI + "\ncurrentURI=" + browser.currentURI.spec + "\n"); 1.278 + 1.279 + let params = aParams || {}; 1.280 + try { 1.281 + let flags = params.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE; 1.282 + let postData = ("postData" in params && params.postData) ? params.postData.value : null; 1.283 + let referrerURI = "referrerURI" in params ? params.referrerURI : null; 1.284 + let charset = "charset" in params ? params.charset : null; 1.285 + dump("loading tab: " + aURI + "\n"); 1.286 + browser.loadURIWithFlags(aURI, flags, referrerURI, charset, postData); 1.287 + } catch(e) { 1.288 + dump("Error: " + e + "\n"); 1.289 + } 1.290 + }, 1.291 + 1.292 + /** 1.293 + * Determine if the given URL is a shortcut/keyword and, if so, expand it 1.294 + * @param aURL String 1.295 + * @param aPostDataRef Out param contains any required post data for a search 1.296 + * @return {Promise} 1.297 + * @result the expanded shortcut, or the original URL if not a shortcut 1.298 + */ 1.299 + getShortcutOrURI: function getShortcutOrURI(aURL, aPostDataRef) { 1.300 + return Task.spawn(function() { 1.301 + if (!aURL) 1.302 + throw new Task.Result(aURL); 1.303 + 1.304 + let shortcutURL = null; 1.305 + let keyword = aURL; 1.306 + let param = ""; 1.307 + 1.308 + let offset = aURL.indexOf(" "); 1.309 + if (offset > 0) { 1.310 + keyword = aURL.substr(0, offset); 1.311 + param = aURL.substr(offset + 1); 1.312 + } 1.313 + 1.314 + if (!aPostDataRef) 1.315 + aPostDataRef = {}; 1.316 + 1.317 + let engine = Services.search.getEngineByAlias(keyword); 1.318 + if (engine) { 1.319 + let submission = engine.getSubmission(param); 1.320 + aPostDataRef.value = submission.postData; 1.321 + throw new Task.Result(submission.uri.spec); 1.322 + } 1.323 + 1.324 + try { 1.325 + [shortcutURL, aPostDataRef.value] = PlacesUtils.getURLAndPostDataForKeyword(keyword); 1.326 + } catch (e) {} 1.327 + 1.328 + if (!shortcutURL) 1.329 + throw new Task.Result(aURL); 1.330 + 1.331 + let postData = ""; 1.332 + if (aPostDataRef.value) 1.333 + postData = unescape(aPostDataRef.value); 1.334 + 1.335 + if (/%s/i.test(shortcutURL) || /%s/i.test(postData)) { 1.336 + let charset = ""; 1.337 + const re = /^(.*)\&mozcharset=([a-zA-Z][_\-a-zA-Z0-9]+)\s*$/; 1.338 + let matches = shortcutURL.match(re); 1.339 + if (matches) 1.340 + [, shortcutURL, charset] = matches; 1.341 + else { 1.342 + // Try to get the saved character-set. 1.343 + try { 1.344 + // makeURI throws if URI is invalid. 1.345 + // Will return an empty string if character-set is not found. 1.346 + charset = yield PlacesUtils.getCharsetForURI(Util.makeURI(shortcutURL)); 1.347 + } catch (e) { dump("--- error " + e + "\n"); } 1.348 + } 1.349 + 1.350 + let encodedParam = ""; 1.351 + if (charset) 1.352 + encodedParam = escape(convertFromUnicode(charset, param)); 1.353 + else // Default charset is UTF-8 1.354 + encodedParam = encodeURIComponent(param); 1.355 + 1.356 + shortcutURL = shortcutURL.replace(/%s/g, encodedParam).replace(/%S/g, param); 1.357 + 1.358 + if (/%s/i.test(postData)) // POST keyword 1.359 + aPostDataRef.value = getPostDataStream(postData, param, encodedParam, "application/x-www-form-urlencoded"); 1.360 + } else if (param) { 1.361 + // This keyword doesn't take a parameter, but one was provided. Just return 1.362 + // the original URL. 1.363 + aPostDataRef.value = null; 1.364 + 1.365 + throw new Task.Result(aURL); 1.366 + } 1.367 + 1.368 + throw new Task.Result(shortcutURL); 1.369 + }); 1.370 + }, 1.371 + 1.372 + /** 1.373 + * Return the currently active <browser> object 1.374 + */ 1.375 + get selectedBrowser() { 1.376 + return (this._selectedTab && this._selectedTab.browser); 1.377 + }, 1.378 + 1.379 + get tabs() { 1.380 + return this._tabs; 1.381 + }, 1.382 + 1.383 + getTabModalPromptBox: function(aBrowser) { 1.384 + let browser = (aBrowser || getBrowser()); 1.385 + let stack = browser.parentNode; 1.386 + let self = this; 1.387 + 1.388 + let promptBox = { 1.389 + appendPrompt : function(args, onCloseCallback) { 1.390 + let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt"); 1.391 + newPrompt.setAttribute("promptType", args.promptType); 1.392 + stack.appendChild(newPrompt); 1.393 + browser.setAttribute("tabmodalPromptShowing", true); 1.394 + newPrompt.clientTop; // style flush to assure binding is attached 1.395 + 1.396 + let tab = self.getTabForBrowser(browser); 1.397 + tab = tab.chromeTab; 1.398 + 1.399 + newPrompt.metroInit(args, tab, onCloseCallback); 1.400 + return newPrompt; 1.401 + }, 1.402 + 1.403 + removePrompt : function(aPrompt) { 1.404 + stack.removeChild(aPrompt); 1.405 + 1.406 + let prompts = this.listPrompts(); 1.407 + if (prompts.length) { 1.408 + let prompt = prompts[prompts.length - 1]; 1.409 + prompt.Dialog.setDefaultFocus(); 1.410 + } else { 1.411 + browser.removeAttribute("tabmodalPromptShowing"); 1.412 + browser.focus(); 1.413 + } 1.414 + }, 1.415 + 1.416 + listPrompts : function(aPrompt) { 1.417 + let els = stack.getElementsByTagNameNS(XUL_NS, "tabmodalprompt"); 1.418 + // NodeList --> real JS array 1.419 + let prompts = Array.slice(els); 1.420 + return prompts; 1.421 + }, 1.422 + }; 1.423 + 1.424 + return promptBox; 1.425 + }, 1.426 + 1.427 + getBrowserForWindowId: function getBrowserForWindowId(aWindowId) { 1.428 + for (let i = 0; i < this.browsers.length; i++) { 1.429 + if (this.browsers[i].contentWindowId == aWindowId) 1.430 + return this.browsers[i]; 1.431 + } 1.432 + return null; 1.433 + }, 1.434 + 1.435 + getBrowserForWindow: function(aWindow) { 1.436 + let windowID = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) 1.437 + .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID; 1.438 + return this.getBrowserForWindowId(windowID); 1.439 + }, 1.440 + 1.441 + getTabForBrowser: function getTabForBrowser(aBrowser) { 1.442 + let tabs = this._tabs; 1.443 + for (let i = 0; i < tabs.length; i++) { 1.444 + if (tabs[i].browser == aBrowser) 1.445 + return tabs[i]; 1.446 + } 1.447 + return null; 1.448 + }, 1.449 + 1.450 + getTabAtIndex: function getTabAtIndex(index) { 1.451 + if (index >= this._tabs.length || index < 0) 1.452 + return null; 1.453 + return this._tabs[index]; 1.454 + }, 1.455 + 1.456 + getTabFromChrome: function getTabFromChrome(chromeTab) { 1.457 + for (var t = 0; t < this._tabs.length; t++) { 1.458 + if (this._tabs[t].chromeTab == chromeTab) 1.459 + return this._tabs[t]; 1.460 + } 1.461 + return null; 1.462 + }, 1.463 + 1.464 + createTabId: function createTabId() { 1.465 + return this._tabId++; 1.466 + }, 1.467 + 1.468 + /** 1.469 + * Create a new tab and add it to the tab list. 1.470 + * 1.471 + * If you are opening a new foreground tab in response to a user action, use 1.472 + * BrowserUI.addAndShowTab which will also show the tab strip. 1.473 + * 1.474 + * @param aURI String specifying the URL to load. 1.475 + * @param aBringFront Boolean (optional) Open the new tab in the foreground? 1.476 + * @param aOwner Tab object (optional) The "parent" of the new tab. 1.477 + * This is the tab responsible for opening the new tab. When the new tab 1.478 + * is closed, we will return to a parent or "sibling" tab if possible. 1.479 + * @param aParams Object (optional) with optional properties: 1.480 + * index: Number specifying where in the tab list to insert the new tab. 1.481 + * private: If true, the new tab should be have Private Browsing active. 1.482 + * flags, postData, charset, referrerURI: See loadURIWithFlags. 1.483 + */ 1.484 + addTab: function browser_addTab(aURI, aBringFront, aOwner, aParams) { 1.485 + let params = aParams || {}; 1.486 + 1.487 + if (aOwner && !('index' in params)) { 1.488 + // Position the new tab to the right of its owner... 1.489 + params.index = this._tabs.indexOf(aOwner) + 1; 1.490 + // ...and to the right of any siblings. 1.491 + while (this._tabs[params.index] && this._tabs[params.index].owner == aOwner) { 1.492 + params.index++; 1.493 + } 1.494 + } 1.495 + 1.496 + let newTab = new Tab(aURI, params, aOwner); 1.497 + 1.498 + if (params.index >= 0) { 1.499 + this._tabs.splice(params.index, 0, newTab); 1.500 + } else { 1.501 + this._tabs.push(newTab); 1.502 + } 1.503 + 1.504 + if (aBringFront) 1.505 + this.selectedTab = newTab; 1.506 + 1.507 + this._announceNewTab(newTab); 1.508 + return newTab; 1.509 + }, 1.510 + 1.511 + closeTab: function closeTab(aTab, aOptions) { 1.512 + let tab = aTab instanceof XULElement ? this.getTabFromChrome(aTab) : aTab; 1.513 + if (!tab) { 1.514 + return; 1.515 + } 1.516 + 1.517 + if (aOptions && "forceClose" in aOptions && aOptions.forceClose) { 1.518 + this._doCloseTab(tab); 1.519 + return; 1.520 + } 1.521 + 1.522 + tab.browser.messageManager.sendAsyncMessage("Browser:CanUnload", {}); 1.523 + }, 1.524 + 1.525 + savePage: function() { 1.526 + ContentAreaUtils.saveDocument(this.selectedBrowser.contentWindow.document); 1.527 + }, 1.528 + 1.529 + /* 1.530 + * helper for addTab related methods. Fires events related to 1.531 + * new tab creation. 1.532 + */ 1.533 + _announceNewTab: function (aTab) { 1.534 + let event = document.createEvent("UIEvents"); 1.535 + event.initUIEvent("TabOpen", true, false, window, 0); 1.536 + aTab.chromeTab.dispatchEvent(event); 1.537 + aTab.browser.messageManager.sendAsyncMessage("Browser:TabOpen"); 1.538 + }, 1.539 + 1.540 + _doCloseTab: function _doCloseTab(aTab) { 1.541 + if (this._tabs.length === 1) { 1.542 + Browser.addTab(this.getHomePage()); 1.543 + } 1.544 + 1.545 + let nextTab = this.getNextTab(aTab); 1.546 + 1.547 + // Tabs owned by the closed tab are now orphaned. 1.548 + this._tabs.forEach(function(item, index, array) { 1.549 + if (item.owner == aTab) 1.550 + item.owner = null; 1.551 + }); 1.552 + 1.553 + // tray tab 1.554 + let event = document.createEvent("Events"); 1.555 + event.initEvent("TabClose", true, false); 1.556 + aTab.chromeTab.dispatchEvent(event); 1.557 + 1.558 + // tab window 1.559 + event = document.createEvent("Events"); 1.560 + event.initEvent("TabClose", true, false); 1.561 + aTab.browser.contentWindow.dispatchEvent(event); 1.562 + 1.563 + aTab.browser.messageManager.sendAsyncMessage("Browser:TabClose"); 1.564 + 1.565 + let container = aTab.chromeTab.parentNode; 1.566 + aTab.destroy(); 1.567 + this._tabs.splice(this._tabs.indexOf(aTab), 1); 1.568 + 1.569 + this.selectedTab = nextTab; 1.570 + 1.571 + event = document.createEvent("Events"); 1.572 + event.initEvent("TabRemove", true, false); 1.573 + container.dispatchEvent(event); 1.574 + }, 1.575 + 1.576 + getNextTab: function getNextTab(aTab) { 1.577 + let tabIndex = this._tabs.indexOf(aTab); 1.578 + if (tabIndex == -1) 1.579 + return null; 1.580 + 1.581 + if (this._selectedTab == aTab || this._selectedTab.chromeTab.hasAttribute("closing")) { 1.582 + let nextTabIndex = tabIndex + 1; 1.583 + let nextTab = null; 1.584 + 1.585 + while (nextTabIndex < this._tabs.length && (!nextTab || nextTab.chromeTab.hasAttribute("closing"))) { 1.586 + nextTab = this.getTabAtIndex(nextTabIndex); 1.587 + nextTabIndex++; 1.588 + } 1.589 + 1.590 + nextTabIndex = tabIndex - 1; 1.591 + while (nextTabIndex >= 0 && (!nextTab || nextTab.chromeTab.hasAttribute("closing"))) { 1.592 + nextTab = this.getTabAtIndex(nextTabIndex); 1.593 + nextTabIndex--; 1.594 + } 1.595 + 1.596 + if (!nextTab || nextTab.chromeTab.hasAttribute("closing")) 1.597 + return null; 1.598 + 1.599 + // If the next tab is not a sibling, switch back to the parent. 1.600 + if (aTab.owner && nextTab.owner != aTab.owner) 1.601 + nextTab = aTab.owner; 1.602 + 1.603 + if (!nextTab) 1.604 + return null; 1.605 + 1.606 + return nextTab; 1.607 + } 1.608 + 1.609 + return this._selectedTab; 1.610 + }, 1.611 + 1.612 + get selectedTab() { 1.613 + return this._selectedTab; 1.614 + }, 1.615 + 1.616 + set selectedTab(tab) { 1.617 + if (tab instanceof XULElement) 1.618 + tab = this.getTabFromChrome(tab); 1.619 + 1.620 + if (!tab) 1.621 + return; 1.622 + 1.623 + if (this._selectedTab == tab) { 1.624 + // Deck does not update its selectedIndex when children 1.625 + // are removed. See bug 602708 1.626 + Elements.browsers.selectedPanel = tab.notification; 1.627 + return; 1.628 + } 1.629 + 1.630 + let isFirstTab = this._selectedTab == null; 1.631 + let lastTab = this._selectedTab; 1.632 + let browser = tab.browser; 1.633 + 1.634 + this._selectedTab = tab; 1.635 + 1.636 + if (lastTab) 1.637 + lastTab.active = false; 1.638 + 1.639 + if (tab) 1.640 + tab.active = true; 1.641 + 1.642 + BrowserUI.update(); 1.643 + 1.644 + if (isFirstTab) { 1.645 + BrowserUI._titleChanged(browser); 1.646 + } else { 1.647 + // Update all of our UI to reflect the new tab's location 1.648 + BrowserUI.updateURI(); 1.649 + 1.650 + let event = document.createEvent("Events"); 1.651 + event.initEvent("TabSelect", true, false); 1.652 + event.lastTab = lastTab; 1.653 + tab.chromeTab.dispatchEvent(event); 1.654 + } 1.655 + 1.656 + tab.lastSelected = Date.now(); 1.657 + }, 1.658 + 1.659 + supportsCommand: function(cmd) { 1.660 + return false; 1.661 + }, 1.662 + 1.663 + isCommandEnabled: function(cmd) { 1.664 + return false; 1.665 + }, 1.666 + 1.667 + doCommand: function(cmd) { 1.668 + }, 1.669 + 1.670 + getNotificationBox: function getNotificationBox(aBrowser) { 1.671 + let browser = aBrowser || this.selectedBrowser; 1.672 + return browser.parentNode.parentNode; 1.673 + }, 1.674 + 1.675 + /** 1.676 + * Handle cert exception message from content. 1.677 + */ 1.678 + _handleCertException: function _handleCertException(aMessage) { 1.679 + let json = aMessage.json; 1.680 + if (json.action == "leave") { 1.681 + // Get the start page from the *default* pref branch, not the user's 1.682 + let url = Browser.getHomePage({ useDefault: true }); 1.683 + this.loadURI(url); 1.684 + } else { 1.685 + // Handle setting an cert exception and reloading the page 1.686 + try { 1.687 + // Add a new SSL exception for this URL 1.688 + let uri = Services.io.newURI(json.url, null, null); 1.689 + let sslExceptions = new SSLExceptions(); 1.690 + 1.691 + if (json.action == "permanent") 1.692 + sslExceptions.addPermanentException(uri, window); 1.693 + else 1.694 + sslExceptions.addTemporaryException(uri, window); 1.695 + } catch (e) { 1.696 + dump("EXCEPTION handle content command: " + e + "\n" ); 1.697 + } 1.698 + 1.699 + // Automatically reload after the exception was added 1.700 + aMessage.target.reload(); 1.701 + } 1.702 + }, 1.703 + 1.704 + /** 1.705 + * Handle blocked site message from content. 1.706 + */ 1.707 + _handleBlockedSite: function _handleBlockedSite(aMessage) { 1.708 + let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter); 1.709 + let json = aMessage.json; 1.710 + switch (json.action) { 1.711 + case "leave": { 1.712 + // Get the start page from the *default* pref branch, not the user's 1.713 + let url = Browser.getHomePage({ useDefault: true }); 1.714 + this.loadURI(url); 1.715 + break; 1.716 + } 1.717 + case "report-malware": { 1.718 + // Get the stop badware "why is this blocked" report url, append the current url, and go there. 1.719 + try { 1.720 + let reportURL = formatter.formatURLPref("browser.safebrowsing.malware.reportURL"); 1.721 + reportURL += json.url; 1.722 + this.loadURI(reportURL); 1.723 + } catch (e) { 1.724 + Cu.reportError("Couldn't get malware report URL: " + e); 1.725 + } 1.726 + break; 1.727 + } 1.728 + case "report-phishing": { 1.729 + // It's a phishing site, just link to the generic information page 1.730 + let url = Services.urlFormatter.formatURLPref("app.support.baseURL"); 1.731 + this.loadURI(url + "phishing-malware"); 1.732 + break; 1.733 + } 1.734 + } 1.735 + }, 1.736 + 1.737 + pinSite: function browser_pinSite() { 1.738 + // Get a path to our app tile 1.739 + var file = Components.classes["@mozilla.org/file/directory_service;1"]. 1.740 + getService(Components.interfaces.nsIProperties). 1.741 + get("CurProcD", Components.interfaces.nsIFile); 1.742 + // Get rid of the current working directory's metro subidr 1.743 + file = file.parent; 1.744 + file.append("tileresources"); 1.745 + file.append("VisualElements_logo.png"); 1.746 + var ios = Components.classes["@mozilla.org/network/io-service;1"]. 1.747 + getService(Components.interfaces.nsIIOService); 1.748 + var uriSpec = ios.newFileURI(file).spec; 1.749 + Services.metro.pinTileAsync(this._currentPageTileID, 1.750 + Browser.selectedBrowser.contentTitle, // short name 1.751 + Browser.selectedBrowser.contentTitle, // display name 1.752 + "-url " + Browser.selectedBrowser.currentURI.spec, 1.753 + uriSpec, uriSpec); 1.754 + }, 1.755 + 1.756 + get _currentPageTileID() { 1.757 + // We use a unique ID per URL, so just use an MD5 hash of the URL as the ID. 1.758 + let hasher = Cc["@mozilla.org/security/hash;1"]. 1.759 + createInstance(Ci.nsICryptoHash); 1.760 + hasher.init(Ci.nsICryptoHash.MD5); 1.761 + let stringStream = Cc["@mozilla.org/io/string-input-stream;1"]. 1.762 + createInstance(Ci.nsIStringInputStream); 1.763 + stringStream.data = Browser.selectedBrowser.currentURI.spec; 1.764 + hasher.updateFromStream(stringStream, -1); 1.765 + let hashASCII = hasher.finish(true); 1.766 + // Replace '/' with a valid filesystem character 1.767 + return ("FFTileID_" + hashASCII).replace('/', '_', 'g'); 1.768 + }, 1.769 + 1.770 + unpinSite: function browser_unpinSite() { 1.771 + if (!Services.metro.immersive) 1.772 + return; 1.773 + 1.774 + Services.metro.unpinTileAsync(this._currentPageTileID); 1.775 + }, 1.776 + 1.777 + isSitePinned: function browser_isSitePinned() { 1.778 + if (!Services.metro.immersive) 1.779 + return false; 1.780 + 1.781 + return Services.metro.isTilePinned(this._currentPageTileID); 1.782 + }, 1.783 + 1.784 + starSite: function browser_starSite(callback) { 1.785 + let uri = this.selectedBrowser.currentURI; 1.786 + let title = this.selectedBrowser.contentTitle; 1.787 + 1.788 + Bookmarks.addForURI(uri, title, callback); 1.789 + }, 1.790 + 1.791 + unstarSite: function browser_unstarSite(callback) { 1.792 + let uri = this.selectedBrowser.currentURI; 1.793 + Bookmarks.removeForURI(uri, callback); 1.794 + }, 1.795 + 1.796 + isSiteStarredAsync: function browser_isSiteStarredAsync(callback) { 1.797 + let uri = this.selectedBrowser.currentURI; 1.798 + Bookmarks.isURIBookmarked(uri, callback); 1.799 + }, 1.800 + 1.801 + /** 1.802 + * Convenience function for getting the scrollbox position off of a 1.803 + * scrollBoxObject interface. Returns the actual values instead of the 1.804 + * wrapping objects. 1.805 + * 1.806 + * @param scroller a scrollBoxObject on which to call scroller.getPosition() 1.807 + */ 1.808 + getScrollboxPosition: function getScrollboxPosition(scroller) { 1.809 + let x = {}; 1.810 + let y = {}; 1.811 + scroller.getPosition(x, y); 1.812 + return new Point(x.value, y.value); 1.813 + }, 1.814 + 1.815 + forceChromeReflow: function forceChromeReflow() { 1.816 + let dummy = getComputedStyle(document.documentElement, "").width; 1.817 + }, 1.818 + 1.819 + receiveMessage: function receiveMessage(aMessage) { 1.820 + let json = aMessage.json; 1.821 + let browser = aMessage.target; 1.822 + 1.823 + switch (aMessage.name) { 1.824 + case "DOMLinkAdded": { 1.825 + // checks for an icon to use for a web app 1.826 + // apple-touch-icon size is 57px and default size is 16px 1.827 + let rel = json.rel.toLowerCase().split(" "); 1.828 + if (rel.indexOf("icon") != -1) { 1.829 + // We use the sizes attribute if available 1.830 + // see http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#rel-icon 1.831 + let size = 16; 1.832 + if (json.sizes) { 1.833 + let sizes = json.sizes.toLowerCase().split(" "); 1.834 + sizes.forEach(function(item) { 1.835 + if (item != "any") { 1.836 + let [w, h] = item.split("x"); 1.837 + size = Math.max(Math.min(w, h), size); 1.838 + } 1.839 + }); 1.840 + } 1.841 + if (size > browser.appIcon.size) { 1.842 + browser.appIcon.href = json.href; 1.843 + browser.appIcon.size = size; 1.844 + } 1.845 + } 1.846 + else if ((rel.indexOf("apple-touch-icon") != -1) && (browser.appIcon.size < 57)) { 1.847 + // XXX should we support apple-touch-icon-precomposed ? 1.848 + // see http://developer.apple.com/safari/library/documentation/appleapplications/reference/safariwebcontent/configuringwebapplications/configuringwebapplications.html 1.849 + browser.appIcon.href = json.href; 1.850 + browser.appIcon.size = 57; 1.851 + } 1.852 + break; 1.853 + } 1.854 + case "Browser:FormSubmit": 1.855 + browser.lastLocation = null; 1.856 + break; 1.857 + 1.858 + case "Browser:CanUnload:Return": { 1.859 + if (json.permit) { 1.860 + let tab = this.getTabForBrowser(browser); 1.861 + BrowserUI.animateClosingTab(tab); 1.862 + } 1.863 + break; 1.864 + } 1.865 + case "Browser:CertException": 1.866 + this._handleCertException(aMessage); 1.867 + break; 1.868 + case "Browser:BlockedSite": 1.869 + this._handleBlockedSite(aMessage); 1.870 + break; 1.871 + } 1.872 + }, 1.873 +}; 1.874 + 1.875 +Browser.MainDragger = function MainDragger() { 1.876 + this._horizontalScrollbar = document.getElementById("horizontal-scroller"); 1.877 + this._verticalScrollbar = document.getElementById("vertical-scroller"); 1.878 + this._scrollScales = { x: 0, y: 0 }; 1.879 + 1.880 + Elements.browsers.addEventListener("PanBegin", this, false); 1.881 + Elements.browsers.addEventListener("PanFinished", this, false); 1.882 +}; 1.883 + 1.884 +Browser.MainDragger.prototype = { 1.885 + isDraggable: function isDraggable(target, scroller) { 1.886 + return { x: true, y: true }; 1.887 + }, 1.888 + 1.889 + dragStart: function dragStart(clientX, clientY, target, scroller) { 1.890 + let browser = getBrowser(); 1.891 + let bcr = browser.getBoundingClientRect(); 1.892 + this._contentView = browser.getViewAt(clientX - bcr.left, clientY - bcr.top); 1.893 + }, 1.894 + 1.895 + dragStop: function dragStop(dx, dy, scroller) { 1.896 + if (this._contentView && this._contentView._updateCacheViewport) 1.897 + this._contentView._updateCacheViewport(); 1.898 + this._contentView = null; 1.899 + }, 1.900 + 1.901 + dragMove: function dragMove(dx, dy, scroller, aIsKinetic) { 1.902 + let doffset = new Point(dx, dy); 1.903 + 1.904 + this._panContent(doffset); 1.905 + 1.906 + if (aIsKinetic && doffset.x != 0) 1.907 + return false; 1.908 + 1.909 + this._updateScrollbars(); 1.910 + 1.911 + return !doffset.equals(dx, dy); 1.912 + }, 1.913 + 1.914 + handleEvent: function handleEvent(aEvent) { 1.915 + let browser = getBrowser(); 1.916 + switch (aEvent.type) { 1.917 + case "PanBegin": { 1.918 + let width = ContentAreaObserver.width, height = ContentAreaObserver.height; 1.919 + let contentWidth = browser.contentDocumentWidth * browser.scale; 1.920 + let contentHeight = browser.contentDocumentHeight * browser.scale; 1.921 + 1.922 + // Allow a small margin on both sides to prevent adding scrollbars 1.923 + // on small viewport approximation 1.924 + const ALLOWED_MARGIN = 5; 1.925 + const SCROLL_CORNER_SIZE = 8; 1.926 + this._scrollScales = { 1.927 + x: (width + ALLOWED_MARGIN) < contentWidth ? (width - SCROLL_CORNER_SIZE) / contentWidth : 0, 1.928 + y: (height + ALLOWED_MARGIN) < contentHeight ? (height - SCROLL_CORNER_SIZE) / contentHeight : 0 1.929 + } 1.930 + 1.931 + this._showScrollbars(); 1.932 + break; 1.933 + } 1.934 + case "PanFinished": 1.935 + this._hideScrollbars(); 1.936 + 1.937 + // Update the scroll position of the content 1.938 + browser._updateCSSViewport(); 1.939 + break; 1.940 + } 1.941 + }, 1.942 + 1.943 + _panContent: function md_panContent(aOffset) { 1.944 + if (this._contentView && !this._contentView.isRoot()) { 1.945 + this._panContentView(this._contentView, aOffset); 1.946 + // XXX we may need to have "escape borders" for iframe panning 1.947 + // XXX does not deal with scrollables within scrollables 1.948 + } 1.949 + // Do content panning 1.950 + this._panContentView(getBrowser().getRootView(), aOffset); 1.951 + }, 1.952 + 1.953 + /** Pan scroller by the given amount. Updates doffset with leftovers. */ 1.954 + _panContentView: function _panContentView(contentView, doffset) { 1.955 + let pos0 = contentView.getPosition(); 1.956 + contentView.scrollBy(doffset.x, doffset.y); 1.957 + let pos1 = contentView.getPosition(); 1.958 + doffset.subtract(pos1.x - pos0.x, pos1.y - pos0.y); 1.959 + }, 1.960 + 1.961 + _updateScrollbars: function _updateScrollbars() { 1.962 + let scaleX = this._scrollScales.x, scaleY = this._scrollScales.y; 1.963 + let contentScroll = Browser.getScrollboxPosition(Browser.contentScrollboxScroller); 1.964 + 1.965 + if (scaleX) 1.966 + this._horizontalScrollbar.style.MozTransform = 1.967 + "translateX(" + Math.round(contentScroll.x * scaleX) + "px)"; 1.968 + 1.969 + if (scaleY) { 1.970 + let y = Math.round(contentScroll.y * scaleY); 1.971 + let x = 0; 1.972 + 1.973 + this._verticalScrollbar.style.MozTransform = 1.974 + "translate(" + x + "px," + y + "px)"; 1.975 + } 1.976 + }, 1.977 + 1.978 + _showScrollbars: function _showScrollbars() { 1.979 + this._updateScrollbars(); 1.980 + let scaleX = this._scrollScales.x, scaleY = this._scrollScales.y; 1.981 + if (scaleX) { 1.982 + this._horizontalScrollbar.width = ContentAreaObserver.width * scaleX; 1.983 + this._horizontalScrollbar.setAttribute("panning", "true"); 1.984 + } 1.985 + 1.986 + if (scaleY) { 1.987 + this._verticalScrollbar.height = ContentAreaObserver.height * scaleY; 1.988 + this._verticalScrollbar.setAttribute("panning", "true"); 1.989 + } 1.990 + }, 1.991 + 1.992 + _hideScrollbars: function _hideScrollbars() { 1.993 + this._scrollScales.x = 0; 1.994 + this._scrollScales.y = 0; 1.995 + this._horizontalScrollbar.removeAttribute("panning"); 1.996 + this._verticalScrollbar.removeAttribute("panning"); 1.997 + this._horizontalScrollbar.removeAttribute("width"); 1.998 + this._verticalScrollbar.removeAttribute("height"); 1.999 + this._horizontalScrollbar.style.MozTransform = ""; 1.1000 + this._verticalScrollbar.style.MozTransform = ""; 1.1001 + } 1.1002 +}; 1.1003 + 1.1004 + 1.1005 +function nsBrowserAccess() { } 1.1006 + 1.1007 +nsBrowserAccess.prototype = { 1.1008 + QueryInterface: function(aIID) { 1.1009 + if (aIID.equals(Ci.nsIBrowserDOMWindow) || aIID.equals(Ci.nsISupports)) 1.1010 + return this; 1.1011 + throw Cr.NS_NOINTERFACE; 1.1012 + }, 1.1013 + 1.1014 + _getOpenAction: function _getOpenAction(aURI, aOpener, aWhere, aContext) { 1.1015 + let where = aWhere; 1.1016 + /* 1.1017 + * aWhere: 1.1018 + * OPEN_DEFAULTWINDOW: default action 1.1019 + * OPEN_CURRENTWINDOW: current window/tab 1.1020 + * OPEN_NEWWINDOW: not allowed, converted to newtab below 1.1021 + * OPEN_NEWTAB: open a new tab 1.1022 + * OPEN_SWITCHTAB: open in an existing tab if it matches, otherwise open 1.1023 + * a new tab. afaict we always open these in the current tab. 1.1024 + */ 1.1025 + if (where == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) { 1.1026 + // query standard browser prefs indicating what to do for default action 1.1027 + switch (aContext) { 1.1028 + // indicates this is an open request from a 3rd party app. 1.1029 + case Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL : 1.1030 + where = Services.prefs.getIntPref("browser.link.open_external"); 1.1031 + break; 1.1032 + // internal request 1.1033 + default : 1.1034 + where = Services.prefs.getIntPref("browser.link.open_newwindow"); 1.1035 + } 1.1036 + } 1.1037 + if (where == Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW) { 1.1038 + Util.dumpLn("Invalid request - we can't open links in new windows."); 1.1039 + where = Ci.nsIBrowserDOMWindow.OPEN_NEWTAB; 1.1040 + } 1.1041 + return where; 1.1042 + }, 1.1043 + 1.1044 + _getBrowser: function _getBrowser(aURI, aOpener, aWhere, aContext) { 1.1045 + let isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL); 1.1046 + // We don't allow externals apps opening chrome docs 1.1047 + if (isExternal && aURI && aURI.schemeIs("chrome")) 1.1048 + return null; 1.1049 + 1.1050 + let location; 1.1051 + let browser; 1.1052 + let loadflags = isExternal ? 1.1053 + Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL : 1.1054 + Ci.nsIWebNavigation.LOAD_FLAGS_NONE; 1.1055 + let openAction = this._getOpenAction(aURI, aOpener, aWhere, aContext); 1.1056 + 1.1057 + if (openAction == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) { 1.1058 + let owner = isExternal ? null : Browser.selectedTab; 1.1059 + let tab = BrowserUI.openLinkInNewTab("about:blank", true, owner); 1.1060 + browser = tab.browser; 1.1061 + } else { 1.1062 + browser = Browser.selectedBrowser; 1.1063 + } 1.1064 + 1.1065 + try { 1.1066 + let referrer; 1.1067 + if (aURI && browser) { 1.1068 + if (aOpener) { 1.1069 + location = aOpener.location; 1.1070 + referrer = Services.io.newURI(location, null, null); 1.1071 + } 1.1072 + browser.loadURIWithFlags(aURI.spec, loadflags, referrer, null, null); 1.1073 + } 1.1074 + browser.focus(); 1.1075 + } catch(e) { } 1.1076 + 1.1077 + return browser; 1.1078 + }, 1.1079 + 1.1080 + openURI: function browser_openURI(aURI, aOpener, aWhere, aContext) { 1.1081 + let browser = this._getBrowser(aURI, aOpener, aWhere, aContext); 1.1082 + return browser ? browser.contentWindow : null; 1.1083 + }, 1.1084 + 1.1085 + openURIInFrame: function browser_openURIInFrame(aURI, aOpener, aWhere, aContext) { 1.1086 + let browser = this._getBrowser(aURI, aOpener, aWhere, aContext); 1.1087 + return browser ? browser.QueryInterface(Ci.nsIFrameLoaderOwner) : null; 1.1088 + }, 1.1089 + 1.1090 + isTabContentWindow: function(aWindow) { 1.1091 + return Browser.browsers.some(function (browser) browser.contentWindow == aWindow); 1.1092 + }, 1.1093 + 1.1094 + get contentWindow() { 1.1095 + return Browser.selectedBrowser.contentWindow; 1.1096 + } 1.1097 +}; 1.1098 + 1.1099 +/** 1.1100 + * Handler for blocked popups, triggered by DOMUpdatePageReport events in browser.xml 1.1101 + */ 1.1102 +var PopupBlockerObserver = { 1.1103 + init: function init() { 1.1104 + Elements.browsers.addEventListener("mousedown", this, true); 1.1105 + }, 1.1106 + 1.1107 + handleEvent: function handleEvent(aEvent) { 1.1108 + switch (aEvent.type) { 1.1109 + case "mousedown": 1.1110 + let box = Browser.getNotificationBox(); 1.1111 + let notification = box.getNotificationWithValue("popup-blocked"); 1.1112 + if (notification && !notification.contains(aEvent.target)) 1.1113 + box.removeNotification(notification); 1.1114 + break; 1.1115 + } 1.1116 + }, 1.1117 + 1.1118 + onUpdatePageReport: function onUpdatePageReport(aEvent) { 1.1119 + var cBrowser = Browser.selectedBrowser; 1.1120 + if (aEvent.originalTarget != cBrowser) 1.1121 + return; 1.1122 + 1.1123 + if (!cBrowser.pageReport) 1.1124 + return; 1.1125 + 1.1126 + let result = Services.perms.testExactPermission(Browser.selectedBrowser.currentURI, "popup"); 1.1127 + if (result == Ci.nsIPermissionManager.DENY_ACTION) 1.1128 + return; 1.1129 + 1.1130 + // Only show the notification again if we've not already shown it. Since 1.1131 + // notifications are per-browser, we don't need to worry about re-adding 1.1132 + // it. 1.1133 + if (!cBrowser.pageReport.reported) { 1.1134 + if (Services.prefs.getBoolPref("privacy.popups.showBrowserMessage")) { 1.1135 + var brandShortName = Strings.brand.GetStringFromName("brandShortName"); 1.1136 + var popupCount = cBrowser.pageReport.length; 1.1137 + 1.1138 + let strings = Strings.browser; 1.1139 + let message = PluralForm.get(popupCount, strings.GetStringFromName("popupWarning.message")) 1.1140 + .replace("#1", brandShortName) 1.1141 + .replace("#2", popupCount); 1.1142 + 1.1143 + var notificationBox = Browser.getNotificationBox(); 1.1144 + var notification = notificationBox.getNotificationWithValue("popup-blocked"); 1.1145 + if (notification) { 1.1146 + notification.label = message; 1.1147 + } 1.1148 + else { 1.1149 + var buttons = [ 1.1150 + { 1.1151 + isDefault: false, 1.1152 + label: strings.GetStringFromName("popupButtonAllowOnce2"), 1.1153 + accessKey: "", 1.1154 + callback: function() { PopupBlockerObserver.showPopupsForSite(); } 1.1155 + }, 1.1156 + { 1.1157 + label: strings.GetStringFromName("popupButtonAlwaysAllow3"), 1.1158 + accessKey: "", 1.1159 + callback: function() { PopupBlockerObserver.allowPopupsForSite(true); } 1.1160 + }, 1.1161 + { 1.1162 + label: strings.GetStringFromName("popupButtonNeverWarn3"), 1.1163 + accessKey: "", 1.1164 + callback: function() { PopupBlockerObserver.allowPopupsForSite(false); } 1.1165 + } 1.1166 + ]; 1.1167 + 1.1168 + const priority = notificationBox.PRIORITY_WARNING_MEDIUM; 1.1169 + notificationBox.appendNotification(message, "popup-blocked", 1.1170 + "chrome://browser/skin/images/infobar-popup.png", 1.1171 + priority, buttons); 1.1172 + } 1.1173 + } 1.1174 + // Record the fact that we've reported this blocked popup, so we don't 1.1175 + // show it again. 1.1176 + cBrowser.pageReport.reported = true; 1.1177 + } 1.1178 + }, 1.1179 + 1.1180 + allowPopupsForSite: function allowPopupsForSite(aAllow) { 1.1181 + var currentURI = Browser.selectedBrowser.currentURI; 1.1182 + Services.perms.add(currentURI, "popup", aAllow 1.1183 + ? Ci.nsIPermissionManager.ALLOW_ACTION 1.1184 + : Ci.nsIPermissionManager.DENY_ACTION); 1.1185 + 1.1186 + Browser.getNotificationBox().removeCurrentNotification(); 1.1187 + }, 1.1188 + 1.1189 + showPopupsForSite: function showPopupsForSite() { 1.1190 + let uri = Browser.selectedBrowser.currentURI; 1.1191 + let pageReport = Browser.selectedBrowser.pageReport; 1.1192 + if (pageReport) { 1.1193 + for (let i = 0; i < pageReport.length; ++i) { 1.1194 + var popupURIspec = pageReport[i].popupWindowURI.spec; 1.1195 + 1.1196 + // Sometimes the popup URI that we get back from the pageReport 1.1197 + // isn't useful (for instance, netscape.com's popup URI ends up 1.1198 + // being "http://www.netscape.com", which isn't really the URI of 1.1199 + // the popup they're trying to show). This isn't going to be 1.1200 + // useful to the user, so we won't create a menu item for it. 1.1201 + if (popupURIspec == "" || !Util.isURLMemorable(popupURIspec) || popupURIspec == uri.spec) 1.1202 + continue; 1.1203 + 1.1204 + let popupFeatures = pageReport[i].popupWindowFeatures; 1.1205 + let popupName = pageReport[i].popupWindowName; 1.1206 + 1.1207 + Browser.addTab(popupURIspec, false, Browser.selectedTab); 1.1208 + } 1.1209 + } 1.1210 + } 1.1211 +}; 1.1212 + 1.1213 +var SessionHistoryObserver = { 1.1214 + observe: function sho_observe(aSubject, aTopic, aData) { 1.1215 + if (aTopic != "browser:purge-session-history") 1.1216 + return; 1.1217 + 1.1218 + let newTab = Browser.addTab("about:start", true); 1.1219 + let tab = Browser._tabs[0]; 1.1220 + while(tab != newTab) { 1.1221 + Browser.closeTab(tab, { forceClose: true } ); 1.1222 + tab = Browser._tabs[0]; 1.1223 + } 1.1224 + 1.1225 + PlacesUtils.history.removeAllPages(); 1.1226 + 1.1227 + // Clear undo history of the URL bar 1.1228 + BrowserUI._edit.editor.transactionManager.clear(); 1.1229 + } 1.1230 +}; 1.1231 + 1.1232 +function getNotificationBox(aBrowser) { 1.1233 + return Browser.getNotificationBox(aBrowser); 1.1234 +} 1.1235 + 1.1236 +function showDownloadManager(aWindowContext, aID, aReason) { 1.1237 + // TODO: Bug 883962: Toggle the downloads infobar as our current "download manager". 1.1238 +} 1.1239 + 1.1240 +function Tab(aURI, aParams, aOwner) { 1.1241 + this._id = null; 1.1242 + this._browser = null; 1.1243 + this._notification = null; 1.1244 + this._loading = false; 1.1245 + this._progressActive = false; 1.1246 + this._progressCount = 0; 1.1247 + this._chromeTab = null; 1.1248 + this._eventDeferred = null; 1.1249 + this._updateThumbnailTimeout = null; 1.1250 + 1.1251 + this._private = false; 1.1252 + if ("private" in aParams) { 1.1253 + this._private = aParams.private; 1.1254 + } else if (aOwner) { 1.1255 + this._private = aOwner._private; 1.1256 + } 1.1257 + 1.1258 + this.owner = aOwner || null; 1.1259 + 1.1260 + // Set to 0 since new tabs that have not been viewed yet are good tabs to 1.1261 + // toss if app needs more memory. 1.1262 + this.lastSelected = 0; 1.1263 + 1.1264 + // aParams is an object that contains some properties for the initial tab 1.1265 + // loading like flags, a referrerURI, a charset or even a postData. 1.1266 + this.create(aURI, aParams || {}, aOwner); 1.1267 + 1.1268 + // default tabs to inactive (i.e. no display port) 1.1269 + this.active = false; 1.1270 +} 1.1271 + 1.1272 +Tab.prototype = { 1.1273 + get browser() { 1.1274 + return this._browser; 1.1275 + }, 1.1276 + 1.1277 + get notification() { 1.1278 + return this._notification; 1.1279 + }, 1.1280 + 1.1281 + get chromeTab() { 1.1282 + return this._chromeTab; 1.1283 + }, 1.1284 + 1.1285 + get isPrivate() { 1.1286 + return this._private; 1.1287 + }, 1.1288 + 1.1289 + get pageShowPromise() { 1.1290 + return this._eventDeferred ? this._eventDeferred.promise : null; 1.1291 + }, 1.1292 + 1.1293 + startLoading: function startLoading() { 1.1294 + if (this._loading) { 1.1295 + let stack = new Error().stack; 1.1296 + throw "Already Loading!\n" + stack; 1.1297 + } 1.1298 + this._loading = true; 1.1299 + }, 1.1300 + 1.1301 + endLoading: function endLoading() { 1.1302 + this._loading = false; 1.1303 + this.updateFavicon(); 1.1304 + }, 1.1305 + 1.1306 + isLoading: function isLoading() { 1.1307 + return this._loading; 1.1308 + }, 1.1309 + 1.1310 + create: function create(aURI, aParams, aOwner) { 1.1311 + this._eventDeferred = Promise.defer(); 1.1312 + 1.1313 + this._chromeTab = Elements.tabList.addTab(aParams.index); 1.1314 + if (this.isPrivate) { 1.1315 + this._chromeTab.setAttribute("private", "true"); 1.1316 + } 1.1317 + 1.1318 + this._id = Browser.createTabId(); 1.1319 + let browser = this._createBrowser(aURI, null); 1.1320 + 1.1321 + let self = this; 1.1322 + function onPageShowEvent(aEvent) { 1.1323 + browser.removeEventListener("pageshow", onPageShowEvent); 1.1324 + if (self._eventDeferred) { 1.1325 + self._eventDeferred.resolve(self); 1.1326 + } 1.1327 + self._eventDeferred = null; 1.1328 + } 1.1329 + browser.addEventListener("pageshow", onPageShowEvent, true); 1.1330 + browser.addEventListener("DOMWindowCreated", this, false); 1.1331 + browser.addEventListener("StartUIChange", this, false); 1.1332 + Elements.browsers.addEventListener("SizeChanged", this, false); 1.1333 + 1.1334 + browser.messageManager.addMessageListener("Content:StateChange", this); 1.1335 + 1.1336 + if (aOwner) 1.1337 + this._copyHistoryFrom(aOwner); 1.1338 + this._loadUsingParams(browser, aURI, aParams); 1.1339 + }, 1.1340 + 1.1341 + updateViewport: function (aEvent) { 1.1342 + // <meta name=viewport> is not yet supported; just use the browser size. 1.1343 + let browser = this.browser; 1.1344 + 1.1345 + // On the start page we add padding to keep the browser above the navbar. 1.1346 + let paddingBottom = parseInt(getComputedStyle(browser).paddingBottom, 10); 1.1347 + let height = browser.clientHeight - paddingBottom; 1.1348 + 1.1349 + browser.setWindowSize(browser.clientWidth, height); 1.1350 + }, 1.1351 + 1.1352 + handleEvent: function (aEvent) { 1.1353 + switch (aEvent.type) { 1.1354 + case "DOMWindowCreated": 1.1355 + case "StartUIChange": 1.1356 + this.updateViewport(); 1.1357 + break; 1.1358 + case "SizeChanged": 1.1359 + this.updateViewport(); 1.1360 + this._delayUpdateThumbnail(); 1.1361 + break; 1.1362 + case "AlertClose": { 1.1363 + if (this == Browser.selectedTab) { 1.1364 + this.updateViewport(); 1.1365 + } 1.1366 + break; 1.1367 + } 1.1368 + } 1.1369 + }, 1.1370 + 1.1371 + receiveMessage: function(aMessage) { 1.1372 + switch (aMessage.name) { 1.1373 + case "Content:StateChange": 1.1374 + // update the thumbnail now... 1.1375 + this.updateThumbnail(); 1.1376 + // ...and in a little while to capture page after load. 1.1377 + if (aMessage.json.stateFlags & Ci.nsIWebProgressListener.STATE_STOP) { 1.1378 + this._delayUpdateThumbnail(); 1.1379 + } 1.1380 + break; 1.1381 + } 1.1382 + }, 1.1383 + 1.1384 + _delayUpdateThumbnail: function() { 1.1385 + clearTimeout(this._updateThumbnailTimeout); 1.1386 + this._updateThumbnailTimeout = setTimeout(() => { 1.1387 + this.updateThumbnail(); 1.1388 + }, kTabThumbnailDelayCapture); 1.1389 + }, 1.1390 + 1.1391 + destroy: function destroy() { 1.1392 + this._browser.messageManager.removeMessageListener("Content:StateChange", this); 1.1393 + this._browser.removeEventListener("DOMWindowCreated", this, false); 1.1394 + this._browser.removeEventListener("StartUIChange", this, false); 1.1395 + Elements.browsers.removeEventListener("SizeChanged", this, false); 1.1396 + clearTimeout(this._updateThumbnailTimeout); 1.1397 + 1.1398 + Elements.tabList.removeTab(this._chromeTab); 1.1399 + this._chromeTab = null; 1.1400 + this._destroyBrowser(); 1.1401 + }, 1.1402 + 1.1403 + resurrect: function resurrect() { 1.1404 + let dead = this._browser; 1.1405 + let active = this.active; 1.1406 + 1.1407 + // Hold onto the session store data 1.1408 + let session = { data: dead.__SS_data, extra: dead.__SS_extdata }; 1.1409 + 1.1410 + // We need this data to correctly create and position the new browser 1.1411 + // If this browser is already a zombie, fallback to the session data 1.1412 + let currentURL = dead.__SS_restore ? session.data.entries[0].url : dead.currentURI.spec; 1.1413 + let sibling = dead.nextSibling; 1.1414 + 1.1415 + // Destory and re-create the browser 1.1416 + this._destroyBrowser(); 1.1417 + let browser = this._createBrowser(currentURL, sibling); 1.1418 + if (active) 1.1419 + this.active = true; 1.1420 + 1.1421 + // Reattach session store data and flag this browser so it is restored on select 1.1422 + browser.__SS_data = session.data; 1.1423 + browser.__SS_extdata = session.extra; 1.1424 + browser.__SS_restore = true; 1.1425 + }, 1.1426 + 1.1427 + _copyHistoryFrom: function _copyHistoryFrom(tab) { 1.1428 + let otherHistory = tab._browser._webNavigation.sessionHistory; 1.1429 + let history = this._browser._webNavigation.sessionHistory; 1.1430 + 1.1431 + // Ensure that history is initialized 1.1432 + history.QueryInterface(Ci.nsISHistoryInternal); 1.1433 + 1.1434 + for (let i = 0, length = otherHistory.index; i <= length; i++) 1.1435 + history.addEntry(otherHistory.getEntryAtIndex(i, false), true); 1.1436 + }, 1.1437 + 1.1438 + _loadUsingParams: function _loadUsingParams(aBrowser, aURI, aParams) { 1.1439 + let flags = aParams.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE; 1.1440 + let postData = ("postData" in aParams && aParams.postData) ? aParams.postData.value : null; 1.1441 + let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null; 1.1442 + let charset = "charset" in aParams ? aParams.charset : null; 1.1443 + aBrowser.loadURIWithFlags(aURI, flags, referrerURI, charset, postData); 1.1444 + }, 1.1445 + 1.1446 + _createBrowser: function _createBrowser(aURI, aInsertBefore) { 1.1447 + if (this._browser) 1.1448 + throw "Browser already exists"; 1.1449 + 1.1450 + // Create a notification box around the browser. Note this includes 1.1451 + // the input overlay we use to shade content from input events when 1.1452 + // we're intercepting touch input. 1.1453 + let notification = this._notification = document.createElement("notificationbox"); 1.1454 + 1.1455 + let browser = this._browser = document.createElement("browser"); 1.1456 + browser.id = "browser-" + this._id; 1.1457 + this._chromeTab.linkedBrowser = browser; 1.1458 + 1.1459 + browser.setAttribute("type", "content-targetable"); 1.1460 + 1.1461 + let useRemote = Services.appinfo.browserTabsRemote; 1.1462 + let useLocal = Util.isLocalScheme(aURI); 1.1463 + browser.setAttribute("remote", (!useLocal && useRemote) ? "true" : "false"); 1.1464 + 1.1465 + // Append the browser to the document, which should start the page load 1.1466 + let stack = document.createElementNS(XUL_NS, "stack"); 1.1467 + stack.className = "browserStack"; 1.1468 + stack.appendChild(browser); 1.1469 + stack.setAttribute("flex", "1"); 1.1470 + notification.appendChild(stack); 1.1471 + Elements.browsers.insertBefore(notification, aInsertBefore); 1.1472 + 1.1473 + notification.dir = "reverse"; 1.1474 + notification.addEventListener("AlertClose", this); 1.1475 + 1.1476 + // let the content area manager know about this browser. 1.1477 + ContentAreaObserver.onBrowserCreated(browser); 1.1478 + 1.1479 + if (this.isPrivate) { 1.1480 + let ctx = browser.docShell.QueryInterface(Ci.nsILoadContext); 1.1481 + ctx.usePrivateBrowsing = true; 1.1482 + } 1.1483 + 1.1484 + // stop about:blank from loading 1.1485 + browser.stop(); 1.1486 + 1.1487 + let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader; 1.1488 + fl.renderMode = Ci.nsIFrameLoader.RENDER_MODE_ASYNC_SCROLL; 1.1489 + 1.1490 + return browser; 1.1491 + }, 1.1492 + 1.1493 + _destroyBrowser: function _destroyBrowser() { 1.1494 + if (this._browser) { 1.1495 + let notification = this._notification; 1.1496 + notification.removeEventListener("AlertClose", this); 1.1497 + let browser = this._browser; 1.1498 + browser.active = false; 1.1499 + 1.1500 + this._notification = null; 1.1501 + this._browser = null; 1.1502 + this._loading = false; 1.1503 + 1.1504 + Elements.browsers.removeChild(notification); 1.1505 + } 1.1506 + }, 1.1507 + 1.1508 + updateThumbnail: function updateThumbnail() { 1.1509 + if (!this.isPrivate) { 1.1510 + PageThumbs.captureToCanvas(this.browser.contentWindow, this._chromeTab.thumbnailCanvas); 1.1511 + } 1.1512 + }, 1.1513 + 1.1514 + updateFavicon: function updateFavicon() { 1.1515 + this._chromeTab.updateFavicon(this._browser.mIconURL); 1.1516 + }, 1.1517 + 1.1518 + set active(aActive) { 1.1519 + if (!this._browser) 1.1520 + return; 1.1521 + 1.1522 + let notification = this._notification; 1.1523 + let browser = this._browser; 1.1524 + 1.1525 + if (aActive) { 1.1526 + notification.classList.add("active-tab-notificationbox"); 1.1527 + browser.setAttribute("type", "content-primary"); 1.1528 + Elements.browsers.selectedPanel = notification; 1.1529 + browser.active = true; 1.1530 + Elements.tabList.selectedTab = this._chromeTab; 1.1531 + browser.focus(); 1.1532 + } else { 1.1533 + notification.classList.remove("active-tab-notificationbox"); 1.1534 + browser.messageManager.sendAsyncMessage("Browser:Blur", { }); 1.1535 + browser.setAttribute("type", "content-targetable"); 1.1536 + browser.active = false; 1.1537 + } 1.1538 + }, 1.1539 + 1.1540 + get active() { 1.1541 + if (!this._browser) 1.1542 + return false; 1.1543 + return this._browser.getAttribute("type") == "content-primary"; 1.1544 + }, 1.1545 + 1.1546 + toString: function() { 1.1547 + return "[Tab " + (this._browser ? this._browser.currentURI.spec : "(no browser)") + "]"; 1.1548 + } 1.1549 +}; 1.1550 + 1.1551 +// Helper used to hide IPC / non-IPC differences for rendering to a canvas 1.1552 +function rendererFactory(aBrowser, aCanvas) { 1.1553 + let wrapper = {}; 1.1554 + 1.1555 + if (aBrowser.contentWindow) { 1.1556 + let ctx = aCanvas.getContext("2d"); 1.1557 + let draw = function(browser, aLeft, aTop, aWidth, aHeight, aColor, aFlags) { 1.1558 + ctx.drawWindow(browser.contentWindow, aLeft, aTop, aWidth, aHeight, aColor, aFlags); 1.1559 + let e = document.createEvent("HTMLEvents"); 1.1560 + e.initEvent("MozAsyncCanvasRender", true, true); 1.1561 + aCanvas.dispatchEvent(e); 1.1562 + }; 1.1563 + wrapper.checkBrowser = function(browser) { 1.1564 + return browser.contentWindow; 1.1565 + }; 1.1566 + wrapper.drawContent = function(callback) { 1.1567 + callback(ctx, draw); 1.1568 + }; 1.1569 + } 1.1570 + else { 1.1571 + let ctx = aCanvas.MozGetIPCContext("2d"); 1.1572 + let draw = function(browser, aLeft, aTop, aWidth, aHeight, aColor, aFlags) { 1.1573 + ctx.asyncDrawXULElement(browser, aLeft, aTop, aWidth, aHeight, aColor, aFlags); 1.1574 + }; 1.1575 + wrapper.checkBrowser = function(browser) { 1.1576 + return !browser.contentWindow; 1.1577 + }; 1.1578 + wrapper.drawContent = function(callback) { 1.1579 + callback(ctx, draw); 1.1580 + }; 1.1581 + } 1.1582 + 1.1583 + return wrapper; 1.1584 +}; 1.1585 + 1.1586 +// Based on ClickEventHandler from /browser/base/content/content.js 1.1587 +let ClickEventHandler = { 1.1588 + init: function () { 1.1589 + gEventListenerService.addSystemEventListener(Elements.browsers, "click", this, true); 1.1590 + }, 1.1591 + 1.1592 + uninit: function () { 1.1593 + gEventListenerService.removeSystemEventListener(Elements.browsers, "click", this, true); 1.1594 + }, 1.1595 + 1.1596 + handleEvent: function (aEvent) { 1.1597 + if (!aEvent.isTrusted || aEvent.defaultPrevented) { 1.1598 + return; 1.1599 + } 1.1600 + let [href, node] = this._hrefAndLinkNodeForClickEvent(aEvent); 1.1601 + if (href && (aEvent.button == 1 || aEvent.ctrlKey)) { 1.1602 + // Open link in a new tab for middle-click or ctrl-click 1.1603 + BrowserUI.openLinkInNewTab(href, aEvent.shiftKey, Browser.selectedTab); 1.1604 + } 1.1605 + }, 1.1606 + 1.1607 + /** 1.1608 + * Extracts linkNode and href for the current click target. 1.1609 + * 1.1610 + * @param event 1.1611 + * The click event. 1.1612 + * @return [href, linkNode]. 1.1613 + * 1.1614 + * @note linkNode will be null if the click wasn't on an anchor 1.1615 + * element (or XLink). 1.1616 + */ 1.1617 + _hrefAndLinkNodeForClickEvent: function(event) { 1.1618 + function isHTMLLink(aNode) { 1.1619 + return ((aNode instanceof content.HTMLAnchorElement && aNode.href) || 1.1620 + (aNode instanceof content.HTMLAreaElement && aNode.href) || 1.1621 + aNode instanceof content.HTMLLinkElement); 1.1622 + } 1.1623 + 1.1624 + let node = event.target; 1.1625 + while (node && !isHTMLLink(node)) { 1.1626 + node = node.parentNode; 1.1627 + } 1.1628 + 1.1629 + if (node) 1.1630 + return [node.href, node]; 1.1631 + 1.1632 + // If there is no linkNode, try simple XLink. 1.1633 + let href, baseURI; 1.1634 + node = event.target; 1.1635 + while (node && !href) { 1.1636 + if (node.nodeType == content.Node.ELEMENT_NODE) { 1.1637 + href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href"); 1.1638 + if (href) 1.1639 + baseURI = node.ownerDocument.baseURIObject; 1.1640 + } 1.1641 + node = node.parentNode; 1.1642 + } 1.1643 + 1.1644 + // In case of XLink, we don't return the node we got href from since 1.1645 + // callers expect <a>-like elements. 1.1646 + // Note: makeURI() will throw if aUri is not a valid URI. 1.1647 + return [href ? Services.io.newURI(href, null, baseURI).spec : null, null]; 1.1648 + } 1.1649 +};