1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/webapprt/content/webapp.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,261 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +const Cc = Components.classes; 1.9 +const Ci = Components.interfaces; 1.10 +const Cu = Components.utils; 1.11 + 1.12 +Cu.import("resource://webapprt/modules/WebappRT.jsm"); 1.13 +Cu.import("resource://gre/modules/Services.jsm"); 1.14 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.15 + 1.16 +XPCOMUtils.defineLazyGetter(this, "gAppBrowser", 1.17 + function() document.getElementById("content")); 1.18 + 1.19 +#ifdef MOZ_CRASHREPORTER 1.20 +XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter", 1.21 + "@mozilla.org/toolkit/crash-reporter;1", 1.22 + "nsICrashReporter"); 1.23 +#endif 1.24 + 1.25 +function isSameOrigin(url) { 1.26 + let origin = Services.io.newURI(url, null, null).prePath; 1.27 + return (origin == WebappRT.config.app.origin); 1.28 +} 1.29 + 1.30 +let progressListener = { 1.31 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, 1.32 + Ci.nsISupportsWeakReference]), 1.33 + onLocationChange: function onLocationChange(progress, request, location, 1.34 + flags) { 1.35 + 1.36 + // Close tooltip (code adapted from /browser/base/content/browser.js) 1.37 + let pageTooltip = document.getElementById("contentAreaTooltip"); 1.38 + let tooltipNode = pageTooltip.triggerNode; 1.39 + if (tooltipNode) { 1.40 + // Optimise for the common case 1.41 + if (progress.isTopLevel) { 1.42 + pageTooltip.hidePopup(); 1.43 + } 1.44 + else { 1.45 + for (let tooltipWindow = tooltipNode.ownerDocument.defaultView; 1.46 + tooltipWindow != tooltipWindow.parent; 1.47 + tooltipWindow = tooltipWindow.parent) { 1.48 + if (tooltipWindow == progress.DOMWindow) { 1.49 + pageTooltip.hidePopup(); 1.50 + break; 1.51 + } 1.52 + } 1.53 + } 1.54 + } 1.55 + 1.56 + // Set the title of the window to the name of the webapp, adding the origin 1.57 + // of the page being loaded if it's from a different origin than the app 1.58 + // (per security bug 741955, which specifies that other-origin pages loaded 1.59 + // in runtime windows must be identified in chrome). 1.60 + let title = WebappRT.config.app.manifest.name; 1.61 + if (!isSameOrigin(location.spec)) { 1.62 + title = location.prePath + " - " + title; 1.63 + } 1.64 + document.documentElement.setAttribute("title", title); 1.65 + }, 1.66 + 1.67 + onStateChange: function onStateChange(aProgress, aRequest, aFlags, aStatus) { 1.68 + if (aRequest instanceof Ci.nsIChannel && 1.69 + aFlags & Ci.nsIWebProgressListener.STATE_START && 1.70 + aFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) { 1.71 + updateCrashReportURL(aRequest.URI); 1.72 + } 1.73 + } 1.74 +}; 1.75 + 1.76 +function onOpenWindow(event) { 1.77 + let name = event.detail.name; 1.78 + 1.79 + if (name == "_blank") { 1.80 + let uri = Services.io.newURI(event.detail.url, null, null); 1.81 + 1.82 + // Direct the URL to the browser. 1.83 + Cc["@mozilla.org/uriloader/external-protocol-service;1"]. 1.84 + getService(Ci.nsIExternalProtocolService). 1.85 + getProtocolHandlerInfo(uri.scheme). 1.86 + launchWithURI(uri); 1.87 + } else { 1.88 + let win = window.openDialog("chrome://webapprt/content/webapp.xul", 1.89 + name, 1.90 + "chrome,dialog=no,resizable," + event.detail.features); 1.91 + 1.92 + win.addEventListener("load", function onLoad() { 1.93 + win.removeEventListener("load", onLoad, false); 1.94 + 1.95 +#ifndef XP_WIN 1.96 +#ifndef XP_MACOSX 1.97 + if (isSameOrigin(event.detail.url)) { 1.98 + // On non-Windows platforms, we open new windows in fullscreen mode 1.99 + // if the opener window is in fullscreen mode, so we hide the menubar; 1.100 + // but on Mac we don't need to hide the menubar. 1.101 + if (document.mozFullScreenElement) { 1.102 + win.document.getElementById("main-menubar").style.display = "none"; 1.103 + } 1.104 + } 1.105 +#endif 1.106 +#endif 1.107 + 1.108 + win.document.getElementById("content").docShell.setIsApp(WebappRT.appID); 1.109 + win.document.getElementById("content").setAttribute("src", event.detail.url); 1.110 + }, false); 1.111 + } 1.112 +} 1.113 + 1.114 +function onLoad() { 1.115 + window.removeEventListener("load", onLoad, false); 1.116 + 1.117 + gAppBrowser.addProgressListener(progressListener, 1.118 + Ci.nsIWebProgress.NOTIFY_LOCATION | 1.119 + Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); 1.120 + 1.121 + updateMenuItems(); 1.122 + 1.123 + gAppBrowser.addEventListener("mozbrowseropenwindow", onOpenWindow); 1.124 +} 1.125 +window.addEventListener("load", onLoad, false); 1.126 + 1.127 +function onUnload() { 1.128 + gAppBrowser.removeProgressListener(progressListener); 1.129 + gAppBrowser.removeEventListener("mozbrowseropenwindow", onOpenWindow); 1.130 +} 1.131 +window.addEventListener("unload", onUnload, false); 1.132 + 1.133 +// Fullscreen handling. 1.134 + 1.135 +#ifndef XP_MACOSX 1.136 +document.addEventListener('mozfullscreenchange', function() { 1.137 + if (document.mozFullScreenElement) { 1.138 + document.getElementById("main-menubar").style.display = "none"; 1.139 + } else { 1.140 + document.getElementById("main-menubar").style.display = ""; 1.141 + } 1.142 +}, false); 1.143 +#endif 1.144 + 1.145 +// On Mac, we dynamically create the label for the Quit menuitem, using 1.146 +// a string property to inject the name of the webapp into it. 1.147 +function updateMenuItems() { 1.148 +#ifdef XP_MACOSX 1.149 + let installRecord = WebappRT.config.app; 1.150 + let manifest = WebappRT.config.app.manifest; 1.151 + let bundle = 1.152 + Services.strings.createBundle("chrome://webapprt/locale/webapp.properties"); 1.153 + let quitLabel = bundle.formatStringFromName("quitApplicationCmdMac.label", 1.154 + [manifest.name], 1); 1.155 + let hideLabel = bundle.formatStringFromName("hideApplicationCmdMac.label", 1.156 + [manifest.name], 1); 1.157 + document.getElementById("menu_FileQuitItem").setAttribute("label", quitLabel); 1.158 + document.getElementById("menu_mac_hide_app").setAttribute("label", hideLabel); 1.159 +#endif 1.160 +} 1.161 + 1.162 +#ifndef XP_MACOSX 1.163 +let gEditUIVisible = true; 1.164 +#endif 1.165 + 1.166 +function updateEditUIVisibility() { 1.167 +#ifndef XP_MACOSX 1.168 + let editMenuPopupState = document.getElementById("menu_EditPopup").state; 1.169 + let contextMenuPopupState = document.getElementById("contentAreaContextMenu").state; 1.170 + 1.171 + // The UI is visible if the Edit menu is opening or open, if the context menu 1.172 + // is open, or if the toolbar has been customized to include the Cut, Copy, 1.173 + // or Paste toolbar buttons. 1.174 + gEditUIVisible = editMenuPopupState == "showing" || 1.175 + editMenuPopupState == "open" || 1.176 + contextMenuPopupState == "showing" || 1.177 + contextMenuPopupState == "open"; 1.178 + 1.179 + // If UI is visible, update the edit commands' enabled state to reflect 1.180 + // whether or not they are actually enabled for the current focus/selection. 1.181 + if (gEditUIVisible) { 1.182 + goUpdateGlobalEditMenuItems(); 1.183 + } 1.184 + 1.185 + // Otherwise, enable all commands, so that keyboard shortcuts still work, 1.186 + // then lazily determine their actual enabled state when the user presses 1.187 + // a keyboard shortcut. 1.188 + else { 1.189 + goSetCommandEnabled("cmd_undo", true); 1.190 + goSetCommandEnabled("cmd_redo", true); 1.191 + goSetCommandEnabled("cmd_cut", true); 1.192 + goSetCommandEnabled("cmd_copy", true); 1.193 + goSetCommandEnabled("cmd_paste", true); 1.194 + goSetCommandEnabled("cmd_selectAll", true); 1.195 + goSetCommandEnabled("cmd_delete", true); 1.196 + goSetCommandEnabled("cmd_switchTextDirection", true); 1.197 + } 1.198 +#endif 1.199 +} 1.200 + 1.201 +function updateCrashReportURL(aURI) { 1.202 +#ifdef MOZ_CRASHREPORTER 1.203 + if (!gCrashReporter.enabled) 1.204 + return; 1.205 + 1.206 + let uri = aURI.clone(); 1.207 + // uri.userPass throws on protocols without the concept of authentication, 1.208 + // like about:, which tests can load, so we catch and ignore an exception. 1.209 + try { 1.210 + if (uri.userPass != "") { 1.211 + uri.userPass = ""; 1.212 + } 1.213 + } catch (e) {} 1.214 + 1.215 + gCrashReporter.annotateCrashReport("URL", uri.spec); 1.216 +#endif 1.217 +} 1.218 + 1.219 +// Context menu handling code. 1.220 +// At the moment there isn't any built-in menu, we only support HTML5 custom 1.221 +// menus. 1.222 + 1.223 +let gContextMenu = null; 1.224 + 1.225 +XPCOMUtils.defineLazyGetter(this, "PageMenu", function() { 1.226 + let tmp = {}; 1.227 + Cu.import("resource://gre/modules/PageMenu.jsm", tmp); 1.228 + return new tmp.PageMenu(); 1.229 +}); 1.230 + 1.231 +function showContextMenu(aEvent, aXULMenu) { 1.232 + if (aEvent.target != aXULMenu) { 1.233 + return true; 1.234 + } 1.235 + 1.236 + gContextMenu = new nsContextMenu(aXULMenu); 1.237 + if (gContextMenu.shouldDisplay) { 1.238 + updateEditUIVisibility(); 1.239 + } 1.240 + 1.241 + return gContextMenu.shouldDisplay; 1.242 +} 1.243 + 1.244 +function hideContextMenu(aEvent, aXULMenu) { 1.245 + if (aEvent.target != aXULMenu) { 1.246 + return; 1.247 + } 1.248 + 1.249 + gContextMenu = null; 1.250 + 1.251 + updateEditUIVisibility(); 1.252 +} 1.253 + 1.254 +function nsContextMenu(aXULMenu) { 1.255 + this.initMenu(aXULMenu); 1.256 +} 1.257 + 1.258 +nsContextMenu.prototype = { 1.259 + initMenu: function(aXULMenu) { 1.260 + this.hasPageMenu = PageMenu.maybeBuildAndAttachMenu(document.popupNode, 1.261 + aXULMenu); 1.262 + this.shouldDisplay = this.hasPageMenu; 1.263 + }, 1.264 +};