From e83bbcc4cd16d2e775b9f6d60c63a9db09e324f8 Mon Sep 17 00:00:00 2001 From: Honza Bambas Date: Wed, 14 Aug 2013 17:17:00 +0200 Subject: [PATCH] Bug 892488 - Get rid of the offline application cache prompt, r=ehsan+jonas --- browser/base/content/test/Makefile.in | 1 + .../test/browser_offlineQuotaNotification.js | 2 + browser/base/content/test/offlineByDefault.js | 17 +++++ .../test/test_offlineNotification.html | 3 + .../base/content/test/test_offline_gzip.html | 2 + content/base/public/nsContentUtils.h | 6 ++ content/base/src/nsContentSink.cpp | 7 +- content/base/src/nsContentUtils.cpp | 32 +++++++++ dom/src/offline/nsDOMOfflineResourceList.cpp | 48 ++++++++++---- dom/src/offline/nsDOMOfflineResourceList.h | 1 - .../offline/460353_iframe_samemanifest.html | 4 ++ .../mochitest/ajax/offline/foreign2.html | 38 +++++------ .../mochitest/ajax/offline/offlineChild.html | 2 +- .../mochitest/ajax/offline/offlineTests.js | 51 ++++++++++++-- .../ajax/offline/test_bug460353.html | 15 ++++- .../ajax/offline/test_bug474696.html | 3 +- .../ajax/offline/test_bug744719-cancel.html | 66 +++++++++---------- .../ajax/offline/test_bug744719.html | 9 ++- .../ajax/offline/test_cancelOfflineCache.html | 1 + .../mochitest/ajax/offline/test_foreign.html | 14 +++- .../ajax/offline/test_lowDeviceStorage.html | 1 - .../ajax/offline/test_updatingManifest.html | 16 +++-- .../android/base/tests/testDoorHanger.java.in | 61 +++++++++++++++++ modules/libpref/src/init/all.js | 2 + .../prefetch/nsOfflineCacheUpdateService.cpp | 14 +--- 25 files changed, 309 insertions(+), 107 deletions(-) create mode 100644 browser/base/content/test/offlineByDefault.js diff --git a/browser/base/content/test/Makefile.in b/browser/base/content/test/Makefile.in index 07974075470..deee2218ade 100644 --- a/browser/base/content/test/Makefile.in +++ b/browser/base/content/test/Makefile.in @@ -35,6 +35,7 @@ MOCHITEST_FILES = \ test_feed_discovery.html \ test_offline_gzip.html \ test_offlineNotification.html \ + offlineByDefault.js \ video.ogg \ $(NULL) diff --git a/browser/base/content/test/browser_offlineQuotaNotification.js b/browser/base/content/test/browser_offlineQuotaNotification.js index a8aba6b9730..507600d3049 100644 --- a/browser/base/content/test/browser_offlineQuotaNotification.js +++ b/browser/base/content/test/browser_offlineQuotaNotification.js @@ -14,6 +14,7 @@ registerCleanupFunction(function() { var principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri); Services.perms.removeFromPrincipal(principal, "offline-app"); Services.prefs.clearUserPref("offline-apps.quota.warn"); + Services.prefs.clearUserPref("offline-apps.allow_by_default"); }); // Check that the "preferences" UI is opened and showing which websites have @@ -70,5 +71,6 @@ function test() { PopupNotifications.panel.firstElementChild.button.click(); }, true); + Services.prefs.setBoolPref("offline-apps.allow_by_default", false); gBrowser.contentWindow.location = URL; } diff --git a/browser/base/content/test/offlineByDefault.js b/browser/base/content/test/offlineByDefault.js new file mode 100644 index 00000000000..72f7e52a01b --- /dev/null +++ b/browser/base/content/test/offlineByDefault.js @@ -0,0 +1,17 @@ +var offlineByDefault = { + defaultValue: false, + prefBranch: SpecialPowers.Cc["@mozilla.org/preferences-service;1"].getService(SpecialPowers.Ci.nsIPrefBranch), + set: function(allow) { + try { + this.defaultValue = this.prefBranch.getBoolPref("offline-apps.allow_by_default"); + } catch (e) { + this.defaultValue = false + } + this.prefBranch.setBoolPref("offline-apps.allow_by_default", allow); + }, + reset: function() { + this.prefBranch.setBoolPref("offline-apps.allow_by_default", this.defaultValue); + } +} + +offlineByDefault.set(false); diff --git a/browser/base/content/test/test_offlineNotification.html b/browser/base/content/test/test_offlineNotification.html index 8c603f15061..ad015a8623e 100644 --- a/browser/base/content/test/test_offlineNotification.html +++ b/browser/base/content/test/test_offlineNotification.html @@ -6,6 +6,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=462856 Test offline app notification + @@ -52,6 +53,8 @@ window.addEventListener("message", function(event) { pm.removeFromPrincipal(principal1, "offline-app"); pm.removeFromPrincipal(principal2, "offline-app"); + offlineByDefault.reset(); + SimpleTest.finish(); } }, false); diff --git a/browser/base/content/test/test_offline_gzip.html b/browser/base/content/test/test_offline_gzip.html index 09713da92ed..1b4a6129961 100644 --- a/browser/base/content/test/test_offline_gzip.html +++ b/browser/base/content/test/test_offline_gzip.html @@ -13,6 +13,7 @@ cache, it can be fetched from the cache successfully. src="/MochiKit/MochiKit.js"> + @@ -46,6 +47,7 @@ function finishTest() { window.removeEventListener("message", handleMessageEvents, false); + offlineByDefault.reset(); SimpleTest.finish(); } diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index 3044829a7f8..383c1fdb84c 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -1503,6 +1503,12 @@ public: */ static bool OfflineAppAllowed(nsIPrincipal *aPrincipal); + /** + * If offline-apps.allow_by_default is true, we set offline-app permission + * for the principal and return true. Otherwise false. + */ + static bool MaybeAllowOfflineAppByDefault(nsIPrincipal *aPrincipal); + /** * Increases the count of blockers preventing scripts from running. * NOTE: You might want to use nsAutoScriptBlocker rather than calling diff --git a/content/base/src/nsContentSink.cpp b/content/base/src/nsContentSink.cpp index af7deb997d2..ad4c1c843e0 100644 --- a/content/base/src/nsContentSink.cpp +++ b/content/base/src/nsContentSink.cpp @@ -1061,8 +1061,11 @@ nsContentSink::ProcessOfflineManifest(const nsAString& aManifestSpec) action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST; } else { - // Only continue if the document has permission to use offline APIs. - if (!nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal())) { + // Only continue if the document has permission to use offline APIs or + // when preferences indicate to permit it automatically. + if (!nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal()) && + !nsContentUtils::MaybeAllowOfflineAppByDefault(mDocument->NodePrincipal()) && + !nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal())) { return; } diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index c39ef5b8b6d..9f40892233f 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -1405,6 +1405,38 @@ nsContentUtils::OfflineAppAllowed(nsIPrincipal *aPrincipal) return NS_SUCCEEDED(rv) && allowed; } +bool +nsContentUtils::MaybeAllowOfflineAppByDefault(nsIPrincipal *aPrincipal) +{ + if (!Preferences::GetRootBranch()) + return false; + + nsresult rv; + + bool allowedByDefault; + rv = Preferences::GetRootBranch()->GetBoolPref( + "offline-apps.allow_by_default", &allowedByDefault); + if (NS_FAILED(rv)) + return false; + + if (!allowedByDefault) + return false; + + nsCOMPtr permissionManager = + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); + if (!permissionManager) + return false; + + rv = permissionManager->AddFromPrincipal( + aPrincipal, "offline-app", nsIPermissionManager::ALLOW_ACTION, + nsIPermissionManager::EXPIRE_NEVER, 0); + if (NS_FAILED(rv)) + return false; + + // We have added the permission + return true; +} + // static void nsContentUtils::Shutdown() diff --git a/dom/src/offline/nsDOMOfflineResourceList.cpp b/dom/src/offline/nsDOMOfflineResourceList.cpp index fa4811db5b5..e6de4f4babc 100644 --- a/dom/src/offline/nsDOMOfflineResourceList.cpp +++ b/dom/src/offline/nsDOMOfflineResourceList.cpp @@ -84,7 +84,6 @@ nsDOMOfflineResourceList::nsDOMOfflineResourceList(nsIURI *aManifestURI, , mManifestURI(aManifestURI) , mDocumentURI(aDocumentURI) , mExposeCacheUpdateStatus(true) - , mDontSetDocumentCache(false) , mStatus(nsIDOMOfflineResourceList::IDLE) , mCachedKeys(nullptr) , mCachedKeysCount(0) @@ -429,6 +428,11 @@ nsDOMOfflineResourceList::GetStatus(uint16_t *aStatus) } } + if (mAvailableApplicationCache) { + *aStatus = nsIDOMOfflineResourceList::UPDATEREADY; + return NS_OK; + } + *aStatus = mStatus; return NS_OK; } @@ -455,10 +459,6 @@ nsDOMOfflineResourceList::Update() window, getter_AddRefs(update)); NS_ENSURE_SUCCESS(rv, rv); - // Since we are invoking the cache update, cache on the document must not - // be updated until swapCache() method is called on applicationCache object. - mDontSetDocumentCache = true; - return NS_OK; } @@ -488,6 +488,8 @@ nsDOMOfflineResourceList::SwapCache() mAvailableApplicationCache->GetClientID(availClientId); if (availClientId == currClientId) return NS_ERROR_DOM_INVALID_STATE_ERR; + } else if (mStatus != OBSOLETE) { + return NS_ERROR_DOM_INVALID_STATE_ERR; } ClearCachedKeys(); @@ -629,16 +631,36 @@ nsDOMOfflineResourceList::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate, NS_IMETHODIMP nsDOMOfflineResourceList::ApplicationCacheAvailable(nsIApplicationCache *aApplicationCache) { - mAvailableApplicationCache = aApplicationCache; + nsCOMPtr currentAppCache = GetDocumentAppCache(); + if (currentAppCache) { + // Document already has a cache, we cannot override it. swapCache is + // here to do it on demand. - if (!mDontSetDocumentCache) { - nsCOMPtr appCacheContainer = - GetDocumentAppCacheContainer(); + // If the newly available cache is identical to the current cache, then + // just ignore this event. + if (aApplicationCache == currentAppCache) { + return NS_OK; + } - if (appCacheContainer) - appCacheContainer->SetApplicationCache(aApplicationCache); + nsCString currClientId, availClientId; + currentAppCache->GetClientID(currClientId); + aApplicationCache->GetClientID(availClientId); + if (availClientId == currClientId) { + return NS_OK; + } + + mAvailableApplicationCache = aApplicationCache; + return NS_OK; } + nsCOMPtr appCacheContainer = + GetDocumentAppCacheContainer(); + + if (appCacheContainer) { + appCacheContainer->SetApplicationCache(aApplicationCache); + } + + mAvailableApplicationCache = nullptr; return NS_OK; } @@ -737,14 +759,12 @@ nsDOMOfflineResourceList::UpdateCompleted(nsIOfflineCacheUpdate *aUpdate) mCacheUpdate->RemoveObserver(this); mCacheUpdate = nullptr; - mDontSetDocumentCache = false; if (NS_SUCCEEDED(rv) && succeeded && !partial) { + mStatus = nsIDOMOfflineResourceList::IDLE; if (isUpgrade) { - mStatus = nsIDOMOfflineResourceList::UPDATEREADY; SendEvent(NS_LITERAL_STRING(UPDATEREADY_STR)); } else { - mStatus = nsIDOMOfflineResourceList::IDLE; SendEvent(NS_LITERAL_STRING(CACHED_STR)); } } diff --git a/dom/src/offline/nsDOMOfflineResourceList.h b/dom/src/offline/nsDOMOfflineResourceList.h index eeb63d2c3a0..40858869cf7 100644 --- a/dom/src/offline/nsDOMOfflineResourceList.h +++ b/dom/src/offline/nsDOMOfflineResourceList.h @@ -157,7 +157,6 @@ private: nsCOMPtr mAvailableApplicationCache; nsCOMPtr mCacheUpdate; bool mExposeCacheUpdateStatus; - bool mDontSetDocumentCache; uint16_t mStatus; // The set of dynamic keys for this application cache object. diff --git a/dom/tests/mochitest/ajax/offline/460353_iframe_samemanifest.html b/dom/tests/mochitest/ajax/offline/460353_iframe_samemanifest.html index 556789792be..de357827b6a 100644 --- a/dom/tests/mochitest/ajax/offline/460353_iframe_samemanifest.html +++ b/dom/tests/mochitest/ajax/offline/460353_iframe_samemanifest.html @@ -12,6 +12,10 @@ applicationCache.oncached = function() { parent.frameOnUpdate("same", true, applicationCache.status); } +applicationCache.onnoupdate = function() { + parent.frameOnUpdate("same", true, applicationCache.status); +} + diff --git a/dom/tests/mochitest/ajax/offline/foreign2.html b/dom/tests/mochitest/ajax/offline/foreign2.html index 133c1748e10..e330c9f5a04 100644 --- a/dom/tests/mochitest/ajax/offline/foreign2.html +++ b/dom/tests/mochitest/ajax/offline/foreign2.html @@ -10,59 +10,57 @@ function manifestUpdated() { +dump(">>>>>>>>>>>>>> MANIFESTUPDATED\n"); var appCacheService = SpecialPowers.Components.classes["@mozilla.org/network/application-cache-service;1"] .getService(SpecialPowers.Ci.nsIApplicationCacheService); var foreign2cache = appCacheService.chooseApplicationCache( "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html", OfflineTest.loadContext()); - OfflineTest.ok(foreign2cache, "Foreign 2 cache present, chosen for foreign2.html"); - OfflineTest.is(foreign2cache.manifestURI.asciiSpec, "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.cacheManifest") + window.opener.OfflineTest.ok(foreign2cache, "Foreign 2 cache present, chosen for foreign2.html"); + window.opener.OfflineTest.is(foreign2cache.manifestURI.asciiSpec, "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.cacheManifest") var foreign1cache = OfflineTest.getActiveCache( "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign1.cacheManifest"); - OfflineTest.ok(foreign1cache, "Foreign 1 cache loaded"); + window.opener.OfflineTest.ok(foreign1cache, "Foreign 1 cache loaded"); foreign1cache.discard(); - OfflineTest.teardown(); - OfflineTest.finish(); + window.opener.onDone(); } function onLoaded() { +dump(">>>>>>>>>>>>>> ONLOADED\n"); var appCacheService = SpecialPowers.Components.classes["@mozilla.org/network/application-cache-service;1"] .getService(SpecialPowers.Ci.nsIApplicationCacheService); - var foreign1cache = OfflineTest.getActiveCache( + var foreign1cache = window.opener.OfflineTest.getActiveCache( "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign1.cacheManifest"); - OfflineTest.ok(foreign1cache, "Foreign 1 cache loaded"); + window.opener.OfflineTest.ok(foreign1cache, "Foreign 1 cache loaded"); - var foreign2cache = OfflineTest.getActiveCache( + var foreign2cache = window.opener.OfflineTest.getActiveCache( "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.cacheManifest"); - OfflineTest.ok(!foreign2cache, "Foreign 2 cache not present"); + window.opener.OfflineTest.ok(!foreign2cache, "Foreign 2 cache not present"); foreign1cache = appCacheService.chooseApplicationCache( - "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html", OfflineTest.loadContext()); - OfflineTest.ok(!foreign1cache, "foreign2.html not chosen from foreign1 cache"); + "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html", window.opener.OfflineTest.loadContext()); + window.opener.OfflineTest.ok(!foreign1cache, "foreign2.html not chosen from foreign1 cache"); try { - OfflineTest.ok(applicationCache.status == SpecialPowers.Ci.nsIDOMOfflineResourceList.UNCACHED, + window.opener.OfflineTest.ok(applicationCache.status == SpecialPowers.Ci.nsIDOMOfflineResourceList.UNCACHED, "there is no associated application cache"); } catch (ex) { - OfflineTest.ok(false, "applicationCache.status must not throw an exception"); + window.opener.OfflineTest.ok(false, "applicationCache.status must not throw an exception"); } } -if (OfflineTest.setup()) -{ - applicationCache.onerror = OfflineTest.failEvent; - applicationCache.onupdateready = OfflineTest.failEvent; - applicationCache.onnoupdate = OfflineTest.failEvent; - applicationCache.oncached = OfflineTest.priv(manifestUpdated); -} +applicationCache.onerror = window.opener.OfflineTest.failEvent; +applicationCache.onupdateready = window.opener.OfflineTest.failEvent; +applicationCache.onnoupdate = window.opener.OfflineTest.failEvent; +applicationCache.oncached = window.opener.OfflineTest.priv(manifestUpdated); diff --git a/dom/tests/mochitest/ajax/offline/offlineChild.html b/dom/tests/mochitest/ajax/offline/offlineChild.html index 38b6fae9ac0..d70a060a164 100644 --- a/dom/tests/mochitest/ajax/offline/offlineChild.html +++ b/dom/tests/mochitest/ajax/offline/offlineChild.html @@ -12,7 +12,7 @@ // (since we don't get those events) function doneLoading() { - window.top.childFinished(); + window.parent.childFinished(); } if (OfflineTest.setupChild()) { diff --git a/dom/tests/mochitest/ajax/offline/offlineTests.js b/dom/tests/mochitest/ajax/offline/offlineTests.js index a39de0a77f7..a5410298b97 100644 --- a/dom/tests/mochitest/ajax/offline/offlineTests.js +++ b/dom/tests/mochitest/ajax/offline/offlineTests.js @@ -58,6 +58,8 @@ fetch: function(callback) var OfflineTest = { +_allowedByDefault: false, + _hasSlave: false, // The window where test results should be sent. @@ -71,6 +73,11 @@ _SJSsStated: [], setupChild: function() { + if (this._allowedByDefault) { + this._masterWindow = window; + return true; + } + if (window.parent.OfflineTest._hasSlave) { return false; } @@ -91,6 +98,17 @@ setup: function() { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); + try { + this._allowedByDefault = prefBranch.getBoolPref("offline-apps.allow_by_default"); + } catch (e) {} + + if (this._allowedByDefault) { + this._masterWindow = window; + + return true; + } + if (!window.opener || !window.opener.OfflineTest || !window.opener.OfflineTest._hasSlave) { // Offline applications must be toplevel windows and have the @@ -154,7 +172,9 @@ teardown: function() finish: function() { - if (this._masterWindow) { + if (this._allowedByDefault) { + SimpleTest.executeSoon(SimpleTest.finish); + } else if (this._masterWindow) { // Slave window: pass control back to master window, close itself. this._masterWindow.SimpleTest.executeSoon(this._masterWindow.OfflineTest.finish); window.close(); @@ -237,12 +257,25 @@ waitForAdd: function(url, onFinished) { manifestURL: function(overload) { - var manifestURLspec = overload || window.top.document.documentElement.getAttribute("manifest"); + var manifestURLspec; + if (overload) { + manifestURLspec = overload; + } else { + var win = window; + while (win && !win.document.documentElement.getAttribute("manifest")) { + if (win == win.parent) + break; + win = win.parent; + } + if (win) + manifestURLspec = win.document.documentElement.getAttribute("manifest"); + } var ios = Cc["@mozilla.org/network/io-service;1"] .getService(Ci.nsIIOService) var baseURI = ios.newURI(window.location.href, null, null); + dump("manifestURLspec=" + manifestURLspec + "\n"); return ios.newURI(manifestURLspec, null, baseURI); }, @@ -261,14 +294,17 @@ getActiveCache: function(overload) var serv = Cc["@mozilla.org/network/application-cache-service;1"] .getService(Ci.nsIApplicationCacheService); var groupID = serv.buildGroupID(this.manifestURL(overload), this.loadContext()); + dump("Getting active cache for " + groupID + " (o=" + overload + ")\n"); return serv.getActiveCache(groupID); }, getActiveSession: function() { var cache = this.getActiveCache(); - if (!cache) + if (!cache) { + dump("No active cache\n"); return null; + } var cacheService = Cc["@mozilla.org/network/cache-service;1"] .getService(Ci.nsICacheService); @@ -302,6 +338,7 @@ checkCacheEntries: function(entries, callback) checkCache: function(url, expectEntry, callback) { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); var cacheSession = this.getActiveSession(); this._checkCache(cacheSession, url, expectEntry, callback); }, @@ -310,11 +347,11 @@ _checkCache: function(cacheSession, url, expectEntry, callback) { if (!cacheSession) { if (expectEntry) { - this.ok(false, url + " should exist in the offline cache"); + this.ok(false, url + " should exist in the offline cache (no session)"); } else { - this.ok(true, url + " should not exist in the offline cache"); + this.ok(true, url + " should not exist in the offline cache (no session)"); } - setTimeout(this.priv(callback), 0); + if (callback) setTimeout(this.priv(callback), 0); return; } @@ -346,7 +383,7 @@ _checkCache: function(cacheSession, url, expectEntry, callback) OfflineTest.ok(false, "got invalid error for " + url); } } - setTimeout(OfflineTest.priv(callback), 0); + if (callback) setTimeout(OfflineTest.priv(callback), 0); } }; diff --git a/dom/tests/mochitest/ajax/offline/test_bug460353.html b/dom/tests/mochitest/ajax/offline/test_bug460353.html index c0ba51aa967..84cb0370443 100644 --- a/dom/tests/mochitest/ajax/offline/test_bug460353.html +++ b/dom/tests/mochitest/ajax/offline/test_bug460353.html @@ -16,10 +16,16 @@