Bug 847518 - window.open(url, "_blank") should open in default browser. r=myk, r=bz

This commit is contained in:
Marco Castelluccio 2014-02-27 11:56:48 -05:00
parent eebfebc89e
commit 7b491bf060
17 changed files with 346 additions and 78 deletions

View File

@ -239,12 +239,11 @@ BrowserElementParent::OpenWindowInProcess(nsIDOMWindow* aOpenerWindow,
nsCOMPtr<nsIDOMWindow> topWindow;
aOpenerWindow->GetScriptableTop(getter_AddRefs(topWindow));
nsCOMPtr<nsIDOMElement> openerFrameDOMElement;
topWindow->GetFrameElement(getter_AddRefs(openerFrameDOMElement));
NS_ENSURE_TRUE(openerFrameDOMElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(topWindow);
nsCOMPtr<Element> openerFrameElement = win->GetFrameElementInternal();
NS_ENSURE_TRUE(openerFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
nsCOMPtr<Element> openerFrameElement =
do_QueryInterface(openerFrameDOMElement);
nsRefPtr<HTMLIFrameElement> popupFrameElement =
CreateIframe(openerFrameElement, aName, /* aRemote = */ false);

View File

@ -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));
}

View File

@ -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);
},
};

View File

@ -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 <a target="_blank"> 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 <browser>
// 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 <a target="_blank"> 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() {

View File

@ -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);
});
}

View File

@ -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);
});
}

View File

@ -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);
});
}

View File

@ -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]

View File

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Window Open Blank Test App</title>
<meta charset="utf-8">
<script>
function onLoad() {
window.open("sample.html", "_blank");
}
</script>
</head>
<body onload="onLoad()">
<h1>Window Open Blank Test App</h1>
</body>
</html>

View File

@ -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"
}

View File

@ -0,0 +1 @@
Content-Type: application/x-web-app-manifest+json

View File

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Window Open Self Test App</title>
<meta charset="utf-8">
<script>
function onLoad() {
window.open("sample.html", "_self");
}
</script>
</head>
<body onload="onLoad()">
<h1>Window Open Self Test App</h1>
</body>
</html>

View File

@ -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"
}

View File

@ -0,0 +1 @@
Content-Type: application/x-web-app-manifest+json

View File

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Window Open Test App</title>
<meta charset="utf-8">
<script>
function onLoad() {
window.open("sample.html");
}
</script>
</head>
<body onload="onLoad()">
<h1>Window Open Test App</h1>
</body>
</html>

View File

@ -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"
}

View File

@ -0,0 +1 @@
Content-Type: application/x-web-app-manifest+json