mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-29 03:44:37 +00:00
Bug 462856 - offline app notification code doesn't handle subframes. r=mconnor, r=bz
This commit is contained in:
parent
19961382d3
commit
19435fea38
@ -1155,6 +1155,10 @@ function prepareForStartup() {
|
||||
// setup our common DOMLinkAdded listener
|
||||
gBrowser.addEventListener("DOMLinkAdded", DOMLinkHandler, false);
|
||||
|
||||
// setup our MozApplicationManifest listener
|
||||
gBrowser.addEventListener("MozApplicationManifest",
|
||||
OfflineApps, false);
|
||||
|
||||
// setup simple gestures support
|
||||
gGestureSupport.init(true);
|
||||
}
|
||||
@ -3875,10 +3879,6 @@ var XULBrowserWindow = {
|
||||
this.endDocumentLoad(aRequest, aStatus);
|
||||
if (!gBrowser.mTabbedMode && !gBrowser.mCurrentBrowser.mIconURL)
|
||||
gBrowser.useDefaultIcon(gBrowser.mCurrentTab);
|
||||
|
||||
if (Components.isSuccessCode(aStatus) &&
|
||||
content.document.documentElement.getAttribute("manifest"))
|
||||
OfflineApps.offlineAppRequested(content);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5334,6 +5334,12 @@ var OfflineApps = {
|
||||
obs.removeObserver(this, "offline-cache-update-completed");
|
||||
},
|
||||
|
||||
handleEvent: function(event) {
|
||||
if (event.type == "MozApplicationManifest") {
|
||||
this.offlineAppRequested(event.originalTarget.defaultView);
|
||||
}
|
||||
},
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// OfflineApps Implementation Methods
|
||||
|
||||
@ -5360,6 +5366,7 @@ var OfflineApps = {
|
||||
},
|
||||
|
||||
_getManifestURI: function(aWindow) {
|
||||
if (!aWindow.document.documentElement) return null;
|
||||
var attr = aWindow.document.documentElement.getAttribute("manifest");
|
||||
if (!attr) return null;
|
||||
|
||||
@ -5481,7 +5488,7 @@ var OfflineApps = {
|
||||
var browser = this._getBrowserForContentWindow(browserWindow,
|
||||
aContentWindow);
|
||||
|
||||
var currentURI = browser.webNavigation.currentURI;
|
||||
var currentURI = aContentWindow.document.documentURIObject;
|
||||
var pm = Cc["@mozilla.org/permissionmanager;1"].
|
||||
getService(Ci.nsIPermissionManager);
|
||||
|
||||
@ -5499,19 +5506,32 @@ var OfflineApps = {
|
||||
// this pref isn't set by default, ignore failures
|
||||
}
|
||||
|
||||
var host = currentURI.asciiHost;
|
||||
var notificationBox = gBrowser.getNotificationBox(browser);
|
||||
var notification = notificationBox.getNotificationWithValue("offline-app-requested");
|
||||
if (!notification) {
|
||||
var notificationID = "offline-app-requested-" + host;
|
||||
var notification = notificationBox.getNotificationWithValue(notificationID);
|
||||
|
||||
if (notification) {
|
||||
notification.documents.push(aContentWindow.document);
|
||||
} else {
|
||||
var bundle_browser = document.getElementById("bundle_browser");
|
||||
|
||||
var buttons = [{
|
||||
label: bundle_browser.getString("offlineApps.allow"),
|
||||
accessKey: bundle_browser.getString("offlineApps.allowAccessKey"),
|
||||
callback: function() { OfflineApps.allowSite(); }
|
||||
callback: function() {
|
||||
for (var i = 0; i < notification.documents.length; i++) {
|
||||
OfflineApps.allowSite(notification.documents[i]);
|
||||
}
|
||||
}
|
||||
},{
|
||||
label: bundle_browser.getString("offlineApps.never"),
|
||||
accessKey: bundle_browser.getString("offlineApps.neverAccessKey"),
|
||||
callback: function() { OfflineApps.disallowSite(); }
|
||||
callback: function() {
|
||||
for (var i = 0; i < notification.documents.length; i++) {
|
||||
OfflineApps.disallowSite(notification.documents[i]);
|
||||
}
|
||||
}
|
||||
},{
|
||||
label: bundle_browser.getString("offlineApps.notNow"),
|
||||
accessKey: bundle_browser.getString("offlineApps.notNowAccessKey"),
|
||||
@ -5520,51 +5540,55 @@ var OfflineApps = {
|
||||
|
||||
const priority = notificationBox.PRIORITY_INFO_LOW;
|
||||
var message = bundle_browser.getFormattedString("offlineApps.available",
|
||||
[ currentURI.host ]);
|
||||
notificationBox.appendNotification(message, "offline-app-requested",
|
||||
"chrome://browser/skin/Info.png",
|
||||
priority, buttons);
|
||||
[ host ]);
|
||||
notification =
|
||||
notificationBox.appendNotification(message, notificationID,
|
||||
"chrome://browser/skin/Info.png",
|
||||
priority, buttons);
|
||||
notification.documents = [ aContentWindow.document ];
|
||||
}
|
||||
},
|
||||
|
||||
allowSite: function() {
|
||||
var currentURI = gBrowser.selectedBrowser.webNavigation.currentURI;
|
||||
allowSite: function(aDocument) {
|
||||
var pm = Cc["@mozilla.org/permissionmanager;1"].
|
||||
getService(Ci.nsIPermissionManager);
|
||||
pm.add(currentURI, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
pm.add(aDocument.documentURIObject, "offline-app",
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
|
||||
// When a site is enabled while loading, <link rel="offline-resource">
|
||||
// resources will start fetching immediately. This one time we need to
|
||||
// do it ourselves.
|
||||
this._startFetching();
|
||||
// When a site is enabled while loading, manifest resources will
|
||||
// start fetching immediately. This one time we need to do it
|
||||
// ourselves.
|
||||
this._startFetching(aDocument);
|
||||
},
|
||||
|
||||
disallowSite: function() {
|
||||
var currentURI = gBrowser.selectedBrowser.webNavigation.currentURI;
|
||||
disallowSite: function(aDocument) {
|
||||
var pm = Cc["@mozilla.org/permissionmanager;1"].
|
||||
getService(Ci.nsIPermissionManager);
|
||||
pm.add(currentURI, "offline-app", Ci.nsIPermissionManager.DENY_ACTION);
|
||||
pm.add(aDocument.documentURIObject, "offline-app",
|
||||
Ci.nsIPermissionManager.DENY_ACTION);
|
||||
},
|
||||
|
||||
manage: function() {
|
||||
openAdvancedPreferences("networkTab");
|
||||
},
|
||||
|
||||
_startFetching: function() {
|
||||
var manifest = content.document.documentElement.getAttribute("manifest");
|
||||
_startFetching: function(aDocument) {
|
||||
if (!aDocument.documentElement)
|
||||
return;
|
||||
|
||||
var manifest = aDocument.documentElement.getAttribute("manifest");
|
||||
if (!manifest)
|
||||
return;
|
||||
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
|
||||
var contentURI = ios.newURI(content.location.href, null, null);
|
||||
var manifestURI = ios.newURI(manifest, content.document.characterSet,
|
||||
contentURI);
|
||||
var manifestURI = ios.newURI(manifest, aDocument.characterSet,
|
||||
aDocument.documentURIObject);
|
||||
|
||||
var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
|
||||
getService(Ci.nsIOfflineCacheUpdateService);
|
||||
updateService.scheduleUpdate(manifestURI, contentURI);
|
||||
updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject);
|
||||
},
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -50,6 +50,13 @@ _TEST_FILES = test_feed_discovery.html \
|
||||
test_contextmenu.html \
|
||||
subtst_contextmenu.html \
|
||||
ctxmenu-image.png \
|
||||
test_offlineNotification.html \
|
||||
offlineChild.html \
|
||||
offlineChild.cacheManifest \
|
||||
offlineChild.cacheManifest^headers^ \
|
||||
offlineChild2.html \
|
||||
offlineChild2.cacheManifest \
|
||||
offlineChild2.cacheManifest^headers^ \
|
||||
$(NULL)
|
||||
|
||||
# browser_bug423833.js disabled temporarily since it's unreliable: bug 428712
|
||||
|
2
browser/base/content/test/offlineChild.cacheManifest
Normal file
2
browser/base/content/test/offlineChild.cacheManifest
Normal file
@ -0,0 +1,2 @@
|
||||
CACHE MANIFEST
|
||||
offlineChild.html
|
@ -0,0 +1 @@
|
||||
Content-Type: text/cache-manifest
|
20
browser/base/content/test/offlineChild.html
Normal file
20
browser/base/content/test/offlineChild.html
Normal file
@ -0,0 +1,20 @@
|
||||
<html manifest="offlineChild.cacheManifest">
|
||||
<head>
|
||||
<title></title>
|
||||
<script type="text/javascript">
|
||||
|
||||
function finish(success) {
|
||||
window.parent.postMessage(success ? "success" : "failure", "*");
|
||||
}
|
||||
|
||||
applicationCache.oncached = function() { finish(true); }
|
||||
applicationCache.onnoupdate = function() { finish(true); }
|
||||
applicationCache.onerror = function() { finish(false); }
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Child</h1>
|
||||
</body>
|
||||
</html>
|
2
browser/base/content/test/offlineChild2.cacheManifest
Normal file
2
browser/base/content/test/offlineChild2.cacheManifest
Normal file
@ -0,0 +1,2 @@
|
||||
CACHE MANIFEST
|
||||
offlineChild2.html
|
@ -0,0 +1 @@
|
||||
Content-Type: text/cache-manifest
|
20
browser/base/content/test/offlineChild2.html
Normal file
20
browser/base/content/test/offlineChild2.html
Normal file
@ -0,0 +1,20 @@
|
||||
<html manifest="offlineChild2.cacheManifest">
|
||||
<head>
|
||||
<title></title>
|
||||
<script type="text/javascript">
|
||||
|
||||
function finish(success) {
|
||||
window.parent.postMessage(success ? "success" : "failure", "*");
|
||||
}
|
||||
|
||||
applicationCache.oncached = function() { finish(true); }
|
||||
applicationCache.onnoupdate = function() { finish(true); }
|
||||
applicationCache.onerror = function() { finish(false); }
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Child</h1>
|
||||
</body>
|
||||
</html>
|
68
browser/base/content/test/test_offlineNotification.html
Normal file
68
browser/base/content/test/test_offlineNotification.html
Normal file
@ -0,0 +1,68 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=462856
|
||||
-->
|
||||
<head>
|
||||
<title>Test offline app notification</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body onload="loaded()">
|
||||
<p id="display">
|
||||
<!-- Load the test frame twice from the same domain,
|
||||
to make sure we get notifications for both -->
|
||||
<iframe name="testFrame" src="offlineChild.html"></iframe>
|
||||
<iframe name="testFrame2" src="offlineChild2.html"></iframe>
|
||||
<!-- Load from another domain to make sure we get a second allow/deny
|
||||
notification -->
|
||||
<iframe name="testFrame3" src="http://example.com/tests/browser/base/content/test/offlineChild.html"></iframe>
|
||||
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var numFinished = 0;
|
||||
|
||||
window.addEventListener("message", function(event) {
|
||||
is(event.data, "success", "Child was successfully cached.");
|
||||
|
||||
if (++numFinished == 3) {
|
||||
// Clean up after ourself
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var pm = Components.classes["@mozilla.org/permissionmanager;1"].
|
||||
getService(Components.interfaces.nsIPermissionManager);
|
||||
|
||||
pm.remove(frames.testFrame.document.documentURIObject.host, "offline-app");
|
||||
pm.remove(frames.testFrame3.document.documentURIObject.host, "offline-app");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}, false);
|
||||
|
||||
function loaded() {
|
||||
// Click the notification bar's "Allow" button. This should kick
|
||||
// off updates, which will eventually lead to getting messages from
|
||||
// the children.
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
|
||||
getService(Components.interfaces.nsIWindowMediator);
|
||||
var win = wm.getMostRecentWindow("navigator:browser");
|
||||
var notificationBox = win.gBrowser.getNotificationBox();
|
||||
|
||||
var notification = notificationBox.getNotificationWithValue("offline-app-requested-localhost");
|
||||
notification.childNodes[0].click();
|
||||
|
||||
notification = notificationBox.getNotificationWithValue("offline-app-requested-example.com");
|
||||
notification.childNodes[0].click();
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -883,6 +883,28 @@ public:
|
||||
PRBool aCancelable,
|
||||
PRBool *aDefaultAction = nsnull);
|
||||
|
||||
/**
|
||||
* This method creates and dispatches a trusted event to the chrome
|
||||
* event handler.
|
||||
* Works only with events which can be created by calling
|
||||
* nsIDOMDocumentEvent::CreateEvent() with parameter "Events".
|
||||
* @param aDocument The document which will be used to create the event,
|
||||
* and whose window's chrome handler will be used to
|
||||
* dispatch the event.
|
||||
* @param aTarget The target of the event, used for event->SetTarget()
|
||||
* @param aEventName The name of the event.
|
||||
* @param aCanBubble Whether the event can bubble.
|
||||
* @param aCancelable Is the event cancelable.
|
||||
* @param aDefaultAction Set to true if default action should be taken,
|
||||
* see nsIDOMEventTarget::DispatchEvent.
|
||||
*/
|
||||
static nsresult DispatchChromeEvent(nsIDocument* aDoc,
|
||||
nsISupports* aTarget,
|
||||
const nsAString& aEventName,
|
||||
PRBool aCanBubble,
|
||||
PRBool aCancelable,
|
||||
PRBool *aDefaultAction = nsnull);
|
||||
|
||||
/**
|
||||
* Determines if an event attribute name (such as onclick) is valid for
|
||||
* a given element type. Types are from the EventNameType enumeration
|
||||
|
@ -3124,6 +3124,51 @@ nsContentUtils::DispatchTrustedEvent(nsIDocument* aDoc, nsISupports* aTarget,
|
||||
return target->DispatchEvent(event, aDefaultAction ? aDefaultAction : &dummy);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentUtils::DispatchChromeEvent(nsIDocument *aDoc,
|
||||
nsISupports *aTarget,
|
||||
const nsAString& aEventName,
|
||||
PRBool aCanBubble, PRBool aCancelable,
|
||||
PRBool *aDefaultAction)
|
||||
{
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aDoc);
|
||||
NS_ENSURE_ARG_POINTER(aDoc->GetWindow());
|
||||
NS_ENSURE_ARG_POINTER(aDoc->GetWindow()->GetChromeEventHandler());
|
||||
|
||||
nsCOMPtr<nsIDOMDocumentEvent> docEvent = do_QueryInterface(aDoc);
|
||||
nsCOMPtr<nsIDOMEventTarget> originalTarget =
|
||||
do_QueryInterface(aTarget);
|
||||
NS_ENSURE_TRUE(docEvent && originalTarget, NS_ERROR_INVALID_ARG);
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
nsresult rv =
|
||||
docEvent->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
|
||||
NS_ENSURE_TRUE(privateEvent, NS_ERROR_FAILURE);
|
||||
|
||||
rv = event->InitEvent(aEventName, aCanBubble, aCancelable);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = privateEvent->SetTarget(originalTarget);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = privateEvent->SetTrusted(PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
rv = aDoc->GetWindow()->GetChromeEventHandler()->DispatchDOMEvent(nsnull,
|
||||
event,
|
||||
nsnull,
|
||||
&status);
|
||||
if (aDefaultAction) {
|
||||
*aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsIContent*
|
||||
nsContentUtils::MatchElementId(nsIContent *aContent, nsIAtom* aId)
|
||||
|
@ -3980,6 +3980,15 @@ nsDocument::DispatchContentLoadedEvents()
|
||||
} while (parent);
|
||||
}
|
||||
|
||||
// If the document has a manifest attribute, fire a MozApplicationManifest
|
||||
// event.
|
||||
nsIContent* root = GetRootContent();
|
||||
if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) {
|
||||
nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
|
||||
NS_LITERAL_STRING("MozApplicationManifest"),
|
||||
PR_TRUE, PR_TRUE);
|
||||
}
|
||||
|
||||
UnblockOnload(PR_TRUE);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user