diff --git a/dom/browser-element/BrowserElementParent.cpp b/dom/browser-element/BrowserElementParent.cpp index 7975dbad73ed..78148376dd3b 100644 --- a/dom/browser-element/BrowserElementParent.cpp +++ b/dom/browser-element/BrowserElementParent.cpp @@ -239,12 +239,11 @@ BrowserElementParent::OpenWindowInProcess(nsIDOMWindow* aOpenerWindow, nsCOMPtr topWindow; aOpenerWindow->GetScriptableTop(getter_AddRefs(topWindow)); - nsCOMPtr openerFrameDOMElement; - topWindow->GetFrameElement(getter_AddRefs(openerFrameDOMElement)); - NS_ENSURE_TRUE(openerFrameDOMElement, BrowserElementParent::OPEN_WINDOW_IGNORED); + nsCOMPtr win = do_QueryInterface(topWindow); + + nsCOMPtr openerFrameElement = win->GetFrameElementInternal(); + NS_ENSURE_TRUE(openerFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED); - nsCOMPtr openerFrameElement = - do_QueryInterface(openerFrameDOMElement); nsRefPtr popupFrameElement = CreateIframe(openerFrameElement, aName, /* aRemote = */ false); diff --git a/webapprt/Startup.jsm b/webapprt/Startup.jsm index 3a04ca8dde5e..345f57e0d00f 100644 --- a/webapprt/Startup.jsm +++ b/webapprt/Startup.jsm @@ -87,8 +87,8 @@ this.startup = function(window) { if (window.document && window.document.getElementById("content")) { deferredWindowLoad.resolve(); } else { - window.addEventListener("load", function onLoad() { - window.removeEventListener("load", onLoad, false); + window.addEventListener("DOMContentLoaded", function onLoad() { + window.removeEventListener("DOMContentLoaded", onLoad, false); deferredWindowLoad.resolve(); }); } @@ -96,12 +96,8 @@ this.startup = function(window) { // Wait for webapps registry loading. yield DOMApplicationRegistry.registryStarted; - // Install/update permissions and get the appID from the webapps registry. - let appID = Ci.nsIScriptSecurityManager.NO_APP_ID; let manifestURL = WebappRT.config.app.manifestURL; if (manifestURL) { - appID = DOMApplicationRegistry.getAppLocalIdByManifestURL(manifestURL); - // On firstrun, set permissions to their default values. // When the webapp runtime is updated, update the permissions. // TODO: Update the permissions when the application is updated. @@ -129,7 +125,15 @@ this.startup = function(window) { let appBrowser = window.document.getElementById("content"); // Set the principal to the correct appID and launch the application. - appBrowser.docShell.setIsApp(appID); + appBrowser.docShell.setIsApp(WebappRT.appID); appBrowser.setAttribute("src", WebappRT.launchURI); + + if (WebappRT.config.app.manifest.fullscreen) { + appBrowser.addEventListener("load", function onLoad() { + appBrowser.removeEventListener("load", onLoad, true); + appBrowser.contentDocument. + documentElement.mozRequestFullScreen(); + }, true); + } }).then(null, Cu.reportError.bind(Cu)); } diff --git a/webapprt/WebappRT.jsm b/webapprt/WebappRT.jsm index e68e5d645eda..093df5f37faa 100644 --- a/webapprt/WebappRT.jsm +++ b/webapprt/WebappRT.jsm @@ -12,10 +12,12 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/AppsUtils.jsm"); -XPCOMUtils.defineLazyGetter(this, "FileUtils", function() { - Cu.import("resource://gre/modules/FileUtils.jsm"); - return FileUtils; -}); +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", + "resource://gre/modules/FileUtils.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, "appsService", + "@mozilla.org/AppsService;1", + "nsIAppsService"); this.WebappRT = { _config: null, @@ -54,4 +56,13 @@ this.WebappRT = { return new ManifestHelper(this.config.app.manifest, this.config.app.origin); }, + + get appID() { + let manifestURL = WebappRT.config.app.manifestURL; + if (!manifestURL) { + return Ci.nsIScriptSecurityManager.NO_APP_ID; + } + + return appsService.getAppLocalIdByManifestURL(manifestURL); + }, }; diff --git a/webapprt/content/webapp.js b/webapprt/content/webapp.js index 1a2ef3588643..b7763a92b319 100644 --- a/webapprt/content/webapp.js +++ b/webapprt/content/webapp.js @@ -19,6 +19,11 @@ XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter", "nsICrashReporter"); #endif +function isSameOrigin(url) { + let origin = Services.io.newURI(url, null, null).prePath; + return (origin == WebappRT.config.app.origin); +} + let progressListener = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]), @@ -50,13 +55,8 @@ let progressListener = { // (per security bug 741955, which specifies that other-origin pages loaded // in runtime windows must be identified in chrome). let title = WebappRT.config.app.manifest.name; - let origin = location.prePath; - if (origin != WebappRT.config.app.origin) { - title = origin + " - " + title; - - // We should exit fullscreen mode if the user navigates off the app - // origin. - document.mozCancelFullScreen(); + if (!isSameOrigin(location.spec)) { + title = location.prePath + " - " + title; } document.documentElement.setAttribute("title", title); }, @@ -70,6 +70,44 @@ let progressListener = { } }; +function onOpenWindow(event) { + let name = event.detail.name; + + if (name == "_blank") { + let uri = Services.io.newURI(event.detail.url, null, null); + + // Direct the URL to the browser. + Cc["@mozilla.org/uriloader/external-protocol-service;1"]. + getService(Ci.nsIExternalProtocolService). + getProtocolHandlerInfo(uri.scheme). + launchWithURI(uri); + } else { + let win = window.openDialog("chrome://webapprt/content/webapp.xul", + name, + "chrome,dialog=no,resizable," + event.detail.features); + + win.addEventListener("load", function onLoad() { + win.removeEventListener("load", onLoad, false); + +#ifndef XP_WIN +#ifndef XP_MACOSX + if (isSameOrigin(event.detail.url)) { + // On non-Windows platforms, we open new windows in fullscreen mode + // if the opener window is in fullscreen mode, so we hide the menubar; + // but on Mac we don't need to hide the menubar. + if (document.mozFullScreenElement) { + win.document.getElementById("main-menubar").style.display = "none"; + } + } +#endif +#endif + + win.document.getElementById("content").docShell.setIsApp(WebappRT.appID); + win.document.getElementById("content").setAttribute("src", event.detail.url); + }, false); + } +} + function onLoad() { window.removeEventListener("load", onLoad, false); @@ -79,39 +117,18 @@ function onLoad() { updateMenuItems(); - // Listen for clicks to redirect to the browser. - // This doesn't capture clicks so content can capture them itself and do - // something different if it doesn't want the default behavior. - gAppBrowser.addEventListener("click", onContentClick, false, true); - - if (WebappRT.config.app.manifest.fullscreen) { - enterFullScreen(); - } + gAppBrowser.addEventListener("mozbrowseropenwindow", onOpenWindow); } window.addEventListener("load", onLoad, false); function onUnload() { gAppBrowser.removeProgressListener(progressListener); + gAppBrowser.removeEventListener("mozbrowseropenwindow", onOpenWindow); } window.addEventListener("unload", onUnload, false); // Fullscreen handling. -function enterFullScreen() { - // We call mozRequestFullScreen here so that the app window goes in - // fullscreen mode as soon as it's loaded and not after the - // content is loaded. - gAppBrowser.mozRequestFullScreen(); - - // We need to call mozRequestFullScreen on the document element too, - // otherwise the app isn't aware of the fullscreen status. - gAppBrowser.addEventListener("load", function onLoad() { - gAppBrowser.removeEventListener("load", onLoad, true); - gAppBrowser.contentDocument. - documentElement.wrappedJSObject.mozRequestFullScreen(); - }, true); -} - #ifndef XP_MACOSX document.addEventListener('mozfullscreenchange', function() { if (document.mozFullScreenElement) { @@ -122,38 +139,6 @@ document.addEventListener('mozfullscreenchange', function() { }, false); #endif -/** - * Direct a click on to the user's default browser. - * - * In the long run, it might be cleaner to move this to an extension of - * nsIWebBrowserChrome3::onBeforeLinkTraversal. - * - * @param {DOMEvent} event the DOM event - **/ -function onContentClick(event) { - let target = event.target; - - if (!(target instanceof HTMLAnchorElement) || - target.getAttribute("target") != "_blank") { - return; - } - - let uri = Services.io.newURI(target.href, - target.ownerDocument.characterSet, - null); - - // Direct the URL to the browser. - Cc["@mozilla.org/uriloader/external-protocol-service;1"]. - getService(Ci.nsIExternalProtocolService). - getProtocolHandlerInfo(uri.scheme). - launchWithURI(uri); - - // Prevent the runtime from loading the URL. We do this after directing it - // to the browser to give the runtime a shot at handling the URL if we fail - // to direct it to the browser for some reason. - event.preventDefault(); -} - // On Mac, we dynamically create the label for the Quit menuitem, using // a string property to inject the name of the webapp into it. function updateMenuItems() { diff --git a/webapprt/test/chrome/browser_window-open-blank.js b/webapprt/test/chrome/browser_window-open-blank.js new file mode 100644 index 000000000000..a3d3c8d5fd66 --- /dev/null +++ b/webapprt/test/chrome/browser_window-open-blank.js @@ -0,0 +1,75 @@ +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +let HandlerService = { + classID: Components.ID("{b4ed9fab-fd39-435a-8e3e-edc3e689e72e}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory, + Ci.nsIExternalProtocolService]), + + createInstance: function(aOuter, aIID) { + if (aOuter) { + throw Cr.NS_ERROR_NO_AGGREGATION; + } + + return this.QueryInterface(aIID); + }, + + init: function() { + Components.manager.nsIComponentRegistrar.registerFactory(this.classID, + "Test Protocol Handler Service", + "@mozilla.org/uriloader/external-protocol-service;1", + this); + }, + + getProtocolHandlerInfo: function(aProtocolScheme) { + let handlerInfoObj = { + launchWithURI: function(aURI) { + is(aURI.spec, + "http://test/webapprtChrome/webapprt/test/chrome/sample.html", + "The app tried to open the link in the default browser"); + + finish(); + } + }; + + return handlerInfoObj; + } +}; + +HandlerService.init(); + +function test() { + waitForExplicitFinish(); + + let progressListener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, + Ci.nsISupportsWeakReference]), + onLocationChange: function(progress, request, location, flags) { + ok(false, "Location changed"); + finish(); + } + }; + + let winObserver = function(win, topic) { + if (topic == "domwindowopened") { + win.addEventListener("load", function onLoadWindow() { + win.removeEventListener("load", onLoadWindow, false); + + if (win.location == "chrome://webapprt/content/webapp.xul") { + ok(false, "New app window opened"); + finish(); + } + }, false); + } + } + + loadWebapp("window-open-blank.webapp", undefined, function() { + gAppBrowser.addProgressListener(progressListener, + Ci.nsIWebProgress.NOTIFY_LOCATION); + }); + + registerCleanupFunction(function() { + Services.ww.unregisterNotification(winObserver); + gAppBrowser.removeProgressListener(progressListener); + }); +} diff --git a/webapprt/test/chrome/browser_window-open-self.js b/webapprt/test/chrome/browser_window-open-self.js new file mode 100644 index 000000000000..2aa4f3fd30d2 --- /dev/null +++ b/webapprt/test/chrome/browser_window-open-self.js @@ -0,0 +1,55 @@ +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://webapprt/modules/WebappRT.jsm"); +let { DOMApplicationRegistry } = + Cu.import("resource://gre/modules/Webapps.jsm", {}); + +function test() { + waitForExplicitFinish(); + + let appID = Ci.nsIScriptSecurityManager.NO_APP_ID; + + let progressListener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, + Ci.nsISupportsWeakReference]), + onLocationChange: function(progress, request, location, flags) { + gAppBrowser.addEventListener("load", function onLoad() { + gAppBrowser.removeEventListener("load", onLoad, true); + + is(DOMApplicationRegistry.getAppLocalIdByManifestURL(WebappRT.config.app.manifestURL), + appID, + "Principal app ID hasn't changed"); + + finish(); + }, true); + } + }; + + let winObserver = function(win, topic) { + if (topic == "domwindowopened") { + win.addEventListener("load", function onLoadWindow() { + win.removeEventListener("load", onLoadWindow, false); + + if (win.location == "chrome://webapprt/content/webapp.xul") { + ok(false, "New app window opened"); + finish(); + } + }, false); + } + } + + loadWebapp("window-open-self.webapp", undefined, function() { + appID = gAppBrowser.contentDocument.defaultView.document.nodePrincipal.appId; + + is(DOMApplicationRegistry.getAppLocalIdByManifestURL(WebappRT.config.app.manifestURL), + appID, + "Principal app ID correct"); + + gAppBrowser.addProgressListener(progressListener, + Ci.nsIWebProgress.NOTIFY_LOCATION); + }); + + registerCleanupFunction(function() { + Services.ww.unregisterNotification(winObserver); + gAppBrowser.removeProgressListener(progressListener); + }); +} diff --git a/webapprt/test/chrome/browser_window-open.js b/webapprt/test/chrome/browser_window-open.js new file mode 100644 index 000000000000..46405223d00e --- /dev/null +++ b/webapprt/test/chrome/browser_window-open.js @@ -0,0 +1,64 @@ +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://webapprt/modules/WebappRT.jsm"); +let { DOMApplicationRegistry } = + Cu.import("resource://gre/modules/Webapps.jsm", {}); + +function test() { + waitForExplicitFinish(); + + let appID = Ci.nsIScriptSecurityManager.NO_APP_ID; + + let progressListener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, + Ci.nsISupportsWeakReference]), + onLocationChange: function(progress, request, location, flags) { + ok(false, "Content redirected") + finish(); + } + }; + + let winObserver = function(win, topic) { + if (topic == "domwindowopened") { + win.addEventListener("load", function onLoadWindow() { + win.removeEventListener("load", onLoadWindow, false); + + if (win.location == "chrome://webapprt/content/webapp.xul") { + let winAppBrowser = win.document.getElementById("content"); + winAppBrowser.addEventListener("load", function onLoadBrowser() { + winAppBrowser.removeEventListener("load", onLoadBrowser, true); + + is(winAppBrowser.getAttribute("src"), + "http://test/webapprtChrome/webapprt/test/chrome/sample.html", + "New window browser has correct src"); + + is(winAppBrowser.contentDocument.defaultView.document.nodePrincipal.appId, + appID, + "New window principal app ID correct"); + + win.close(); + + finish(); + }, true); + } + }, false); + } + } + + Services.ww.registerNotification(winObserver); + + loadWebapp("window-open.webapp", undefined, function() { + appID = gAppBrowser.contentDocument.defaultView.document.nodePrincipal.appId; + + is(DOMApplicationRegistry.getAppLocalIdByManifestURL(WebappRT.config.app.manifestURL), + appID, + "Principal app ID correct"); + + gAppBrowser.addProgressListener(progressListener, + Ci.nsIWebProgress.NOTIFY_LOCATION); + }); + + registerCleanupFunction(function() { + Services.ww.unregisterNotification(winObserver); + gAppBrowser.removeProgressListener(progressListener); + }); +} diff --git a/webapprt/test/chrome/webapprt.ini b/webapprt/test/chrome/webapprt.ini index 26d7806c9cac..715f8e4041f2 100644 --- a/webapprt/test/chrome/webapprt.ini +++ b/webapprt/test/chrome/webapprt.ini @@ -29,6 +29,15 @@ support-files = getUserMedia.webapp getUserMedia.webapp^headers^ getUserMedia.html + window-open-self.webapp + window-open-self.webapp^headers^ + window-open-self.html + window-open.webapp + window-open.webapp^headers^ + window-open.html + window-open-blank.webapp + window-open-blank.webapp^headers^ + window-open-blank.html [browser_sample.js] @@ -41,3 +50,6 @@ support-files = [browser_mozpay.js] [browser_getUserMedia.js] skip-if = true +[browser_window-open-self.js] +[browser_window-open.js] +[browser_window-open-blank.js] diff --git a/webapprt/test/chrome/window-open-blank.html b/webapprt/test/chrome/window-open-blank.html new file mode 100644 index 000000000000..de7f53fc6ce6 --- /dev/null +++ b/webapprt/test/chrome/window-open-blank.html @@ -0,0 +1,15 @@ + + + + Window Open Blank Test App + + + + +

Window Open Blank Test App

+ + diff --git a/webapprt/test/chrome/window-open-blank.webapp b/webapprt/test/chrome/window-open-blank.webapp new file mode 100644 index 000000000000..c3ab9f9a7431 --- /dev/null +++ b/webapprt/test/chrome/window-open-blank.webapp @@ -0,0 +1,5 @@ +{ + "name": "Window Open Blank Test App", + "description": "an app for testing window.open(url, '_blank')", + "launch_path": "/webapprtChrome/webapprt/test/chrome/window-open-blank.html" +} diff --git a/webapprt/test/chrome/window-open-blank.webapp^headers^ b/webapprt/test/chrome/window-open-blank.webapp^headers^ new file mode 100644 index 000000000000..a2367b11c78b --- /dev/null +++ b/webapprt/test/chrome/window-open-blank.webapp^headers^ @@ -0,0 +1 @@ +Content-Type: application/x-web-app-manifest+json diff --git a/webapprt/test/chrome/window-open-self.html b/webapprt/test/chrome/window-open-self.html new file mode 100644 index 000000000000..60b95ec90ee1 --- /dev/null +++ b/webapprt/test/chrome/window-open-self.html @@ -0,0 +1,15 @@ + + + + Window Open Self Test App + + + + +

Window Open Self Test App

+ + diff --git a/webapprt/test/chrome/window-open-self.webapp b/webapprt/test/chrome/window-open-self.webapp new file mode 100644 index 000000000000..8e6db7ec2698 --- /dev/null +++ b/webapprt/test/chrome/window-open-self.webapp @@ -0,0 +1,5 @@ +{ + "name": "Window Open Self Test App", + "description": "an app for testing window.open(url, '_self')", + "launch_path": "/webapprtChrome/webapprt/test/chrome/window-open-self.html" +} diff --git a/webapprt/test/chrome/window-open-self.webapp^headers^ b/webapprt/test/chrome/window-open-self.webapp^headers^ new file mode 100644 index 000000000000..a2367b11c78b --- /dev/null +++ b/webapprt/test/chrome/window-open-self.webapp^headers^ @@ -0,0 +1 @@ +Content-Type: application/x-web-app-manifest+json diff --git a/webapprt/test/chrome/window-open.html b/webapprt/test/chrome/window-open.html new file mode 100644 index 000000000000..5673512e4280 --- /dev/null +++ b/webapprt/test/chrome/window-open.html @@ -0,0 +1,15 @@ + + + + Window Open Test App + + + + +

Window Open Test App

+ + diff --git a/webapprt/test/chrome/window-open.webapp b/webapprt/test/chrome/window-open.webapp new file mode 100644 index 000000000000..fd0eda722e79 --- /dev/null +++ b/webapprt/test/chrome/window-open.webapp @@ -0,0 +1,5 @@ +{ + "name": "Window Open Test App", + "description": "an app for testing window.open", + "launch_path": "/webapprtChrome/webapprt/test/chrome/window-open.html" +} diff --git a/webapprt/test/chrome/window-open.webapp^headers^ b/webapprt/test/chrome/window-open.webapp^headers^ new file mode 100644 index 000000000000..a2367b11c78b --- /dev/null +++ b/webapprt/test/chrome/window-open.webapp^headers^ @@ -0,0 +1 @@ +Content-Type: application/x-web-app-manifest+json