michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: const Cu = Components.utils; michael@0: michael@0: Cu.import("resource://webapprt/modules/WebappRT.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyGetter(this, "gAppBrowser", michael@0: function() document.getElementById("content")); michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter", michael@0: "@mozilla.org/toolkit/crash-reporter;1", michael@0: "nsICrashReporter"); michael@0: #endif michael@0: michael@0: function isSameOrigin(url) { michael@0: let origin = Services.io.newURI(url, null, null).prePath; michael@0: return (origin == WebappRT.config.app.origin); michael@0: } michael@0: michael@0: let progressListener = { michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, michael@0: Ci.nsISupportsWeakReference]), michael@0: onLocationChange: function onLocationChange(progress, request, location, michael@0: flags) { michael@0: michael@0: // Close tooltip (code adapted from /browser/base/content/browser.js) michael@0: let pageTooltip = document.getElementById("contentAreaTooltip"); michael@0: let tooltipNode = pageTooltip.triggerNode; michael@0: if (tooltipNode) { michael@0: // Optimise for the common case michael@0: if (progress.isTopLevel) { michael@0: pageTooltip.hidePopup(); michael@0: } michael@0: else { michael@0: for (let tooltipWindow = tooltipNode.ownerDocument.defaultView; michael@0: tooltipWindow != tooltipWindow.parent; michael@0: tooltipWindow = tooltipWindow.parent) { michael@0: if (tooltipWindow == progress.DOMWindow) { michael@0: pageTooltip.hidePopup(); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Set the title of the window to the name of the webapp, adding the origin michael@0: // of the page being loaded if it's from a different origin than the app michael@0: // (per security bug 741955, which specifies that other-origin pages loaded michael@0: // in runtime windows must be identified in chrome). michael@0: let title = WebappRT.config.app.manifest.name; michael@0: if (!isSameOrigin(location.spec)) { michael@0: title = location.prePath + " - " + title; michael@0: } michael@0: document.documentElement.setAttribute("title", title); michael@0: }, michael@0: michael@0: onStateChange: function onStateChange(aProgress, aRequest, aFlags, aStatus) { michael@0: if (aRequest instanceof Ci.nsIChannel && michael@0: aFlags & Ci.nsIWebProgressListener.STATE_START && michael@0: aFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) { michael@0: updateCrashReportURL(aRequest.URI); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: function onOpenWindow(event) { michael@0: let name = event.detail.name; michael@0: michael@0: if (name == "_blank") { michael@0: let uri = Services.io.newURI(event.detail.url, null, null); michael@0: michael@0: // Direct the URL to the browser. michael@0: Cc["@mozilla.org/uriloader/external-protocol-service;1"]. michael@0: getService(Ci.nsIExternalProtocolService). michael@0: getProtocolHandlerInfo(uri.scheme). michael@0: launchWithURI(uri); michael@0: } else { michael@0: let win = window.openDialog("chrome://webapprt/content/webapp.xul", michael@0: name, michael@0: "chrome,dialog=no,resizable," + event.detail.features); michael@0: michael@0: win.addEventListener("load", function onLoad() { michael@0: win.removeEventListener("load", onLoad, false); michael@0: michael@0: #ifndef XP_WIN michael@0: #ifndef XP_MACOSX michael@0: if (isSameOrigin(event.detail.url)) { michael@0: // On non-Windows platforms, we open new windows in fullscreen mode michael@0: // if the opener window is in fullscreen mode, so we hide the menubar; michael@0: // but on Mac we don't need to hide the menubar. michael@0: if (document.mozFullScreenElement) { michael@0: win.document.getElementById("main-menubar").style.display = "none"; michael@0: } michael@0: } michael@0: #endif michael@0: #endif michael@0: michael@0: win.document.getElementById("content").docShell.setIsApp(WebappRT.appID); michael@0: win.document.getElementById("content").setAttribute("src", event.detail.url); michael@0: }, false); michael@0: } michael@0: } michael@0: michael@0: function onLoad() { michael@0: window.removeEventListener("load", onLoad, false); michael@0: michael@0: gAppBrowser.addProgressListener(progressListener, michael@0: Ci.nsIWebProgress.NOTIFY_LOCATION | michael@0: Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); michael@0: michael@0: updateMenuItems(); michael@0: michael@0: gAppBrowser.addEventListener("mozbrowseropenwindow", onOpenWindow); michael@0: } michael@0: window.addEventListener("load", onLoad, false); michael@0: michael@0: function onUnload() { michael@0: gAppBrowser.removeProgressListener(progressListener); michael@0: gAppBrowser.removeEventListener("mozbrowseropenwindow", onOpenWindow); michael@0: } michael@0: window.addEventListener("unload", onUnload, false); michael@0: michael@0: // Fullscreen handling. michael@0: michael@0: #ifndef XP_MACOSX michael@0: document.addEventListener('mozfullscreenchange', function() { michael@0: if (document.mozFullScreenElement) { michael@0: document.getElementById("main-menubar").style.display = "none"; michael@0: } else { michael@0: document.getElementById("main-menubar").style.display = ""; michael@0: } michael@0: }, false); michael@0: #endif michael@0: michael@0: // On Mac, we dynamically create the label for the Quit menuitem, using michael@0: // a string property to inject the name of the webapp into it. michael@0: function updateMenuItems() { michael@0: #ifdef XP_MACOSX michael@0: let installRecord = WebappRT.config.app; michael@0: let manifest = WebappRT.config.app.manifest; michael@0: let bundle = michael@0: Services.strings.createBundle("chrome://webapprt/locale/webapp.properties"); michael@0: let quitLabel = bundle.formatStringFromName("quitApplicationCmdMac.label", michael@0: [manifest.name], 1); michael@0: let hideLabel = bundle.formatStringFromName("hideApplicationCmdMac.label", michael@0: [manifest.name], 1); michael@0: document.getElementById("menu_FileQuitItem").setAttribute("label", quitLabel); michael@0: document.getElementById("menu_mac_hide_app").setAttribute("label", hideLabel); michael@0: #endif michael@0: } michael@0: michael@0: #ifndef XP_MACOSX michael@0: let gEditUIVisible = true; michael@0: #endif michael@0: michael@0: function updateEditUIVisibility() { michael@0: #ifndef XP_MACOSX michael@0: let editMenuPopupState = document.getElementById("menu_EditPopup").state; michael@0: let contextMenuPopupState = document.getElementById("contentAreaContextMenu").state; michael@0: michael@0: // The UI is visible if the Edit menu is opening or open, if the context menu michael@0: // is open, or if the toolbar has been customized to include the Cut, Copy, michael@0: // or Paste toolbar buttons. michael@0: gEditUIVisible = editMenuPopupState == "showing" || michael@0: editMenuPopupState == "open" || michael@0: contextMenuPopupState == "showing" || michael@0: contextMenuPopupState == "open"; michael@0: michael@0: // If UI is visible, update the edit commands' enabled state to reflect michael@0: // whether or not they are actually enabled for the current focus/selection. michael@0: if (gEditUIVisible) { michael@0: goUpdateGlobalEditMenuItems(); michael@0: } michael@0: michael@0: // Otherwise, enable all commands, so that keyboard shortcuts still work, michael@0: // then lazily determine their actual enabled state when the user presses michael@0: // a keyboard shortcut. michael@0: else { michael@0: goSetCommandEnabled("cmd_undo", true); michael@0: goSetCommandEnabled("cmd_redo", true); michael@0: goSetCommandEnabled("cmd_cut", true); michael@0: goSetCommandEnabled("cmd_copy", true); michael@0: goSetCommandEnabled("cmd_paste", true); michael@0: goSetCommandEnabled("cmd_selectAll", true); michael@0: goSetCommandEnabled("cmd_delete", true); michael@0: goSetCommandEnabled("cmd_switchTextDirection", true); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: function updateCrashReportURL(aURI) { michael@0: #ifdef MOZ_CRASHREPORTER michael@0: if (!gCrashReporter.enabled) michael@0: return; michael@0: michael@0: let uri = aURI.clone(); michael@0: // uri.userPass throws on protocols without the concept of authentication, michael@0: // like about:, which tests can load, so we catch and ignore an exception. michael@0: try { michael@0: if (uri.userPass != "") { michael@0: uri.userPass = ""; michael@0: } michael@0: } catch (e) {} michael@0: michael@0: gCrashReporter.annotateCrashReport("URL", uri.spec); michael@0: #endif michael@0: } michael@0: michael@0: // Context menu handling code. michael@0: // At the moment there isn't any built-in menu, we only support HTML5 custom michael@0: // menus. michael@0: michael@0: let gContextMenu = null; michael@0: michael@0: XPCOMUtils.defineLazyGetter(this, "PageMenu", function() { michael@0: let tmp = {}; michael@0: Cu.import("resource://gre/modules/PageMenu.jsm", tmp); michael@0: return new tmp.PageMenu(); michael@0: }); michael@0: michael@0: function showContextMenu(aEvent, aXULMenu) { michael@0: if (aEvent.target != aXULMenu) { michael@0: return true; michael@0: } michael@0: michael@0: gContextMenu = new nsContextMenu(aXULMenu); michael@0: if (gContextMenu.shouldDisplay) { michael@0: updateEditUIVisibility(); michael@0: } michael@0: michael@0: return gContextMenu.shouldDisplay; michael@0: } michael@0: michael@0: function hideContextMenu(aEvent, aXULMenu) { michael@0: if (aEvent.target != aXULMenu) { michael@0: return; michael@0: } michael@0: michael@0: gContextMenu = null; michael@0: michael@0: updateEditUIVisibility(); michael@0: } michael@0: michael@0: function nsContextMenu(aXULMenu) { michael@0: this.initMenu(aXULMenu); michael@0: } michael@0: michael@0: nsContextMenu.prototype = { michael@0: initMenu: function(aXULMenu) { michael@0: this.hasPageMenu = PageMenu.maybeBuildAndAttachMenu(document.popupNode, michael@0: aXULMenu); michael@0: this.shouldDisplay = this.hasPageMenu; michael@0: }, michael@0: };