Bug 918880 - MaybeAllowOfflineAppByDefault must work on child processes. r=jduell, a=koi+
authorHonza Bambas <honzab.moz@firemni.cz>
Tue, 19 Nov 2013 23:15:59 +0100
changeset 175467 f5bccb6ba19677c91fca8184dfe1ee6c57e99961
parent 175466 2f891e8ee3c4cf9fa6d56ed651e962323a6d6349
child 175468 32876e74afaedfd590da8f457534466717308b88
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjduell, koi
bugs918880
milestone28.0a2
Bug 918880 - MaybeAllowOfflineAppByDefault must work on child processes. r=jduell, a=koi+
b2g/chrome/content/shell.js
content/base/public/nsContentUtils.h
content/base/src/nsContentSink.cpp
content/base/src/nsContentUtils.cpp
dom/ipc/PBrowser.ipdl
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
uriloader/prefetch/nsIOfflineCacheUpdate.idl
uriloader/prefetch/nsOfflineCacheUpdate.h
uriloader/prefetch/nsOfflineCacheUpdateService.cpp
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -505,16 +505,19 @@ var shell = {
               return;
             }
             return;
           }
 
           Services.perms.addFromPrincipal(principal, 'offline-app',
                                           Ci.nsIPermissionManager.ALLOW_ACTION);
 
+          let documentURI = Services.io.newURI(contentWindow.document.documentURI,
+                                               null,
+                                               null);
           let manifestURI = Services.io.newURI(manifest, null, documentURI);
           let updateService = Cc['@mozilla.org/offlinecacheupdate-service;1']
                               .getService(Ci.nsIOfflineCacheUpdateService);
           updateService.scheduleUpdate(manifestURI, documentURI, window);
         } catch (e) {
           dump('Error while creating offline cache: ' + e + '\n');
         }
         break;
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1457,17 +1457,17 @@ public:
    * Check whether an application should be allowed to use offline APIs.
    */
   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);
+  static bool MaybeAllowOfflineAppByDefault(nsIPrincipal *aPrincipal, nsIDOMWindow *aWindow);
 
   /**
    * Increases the count of blockers preventing scripts from running.
    * NOTE: You might want to use nsAutoScriptBlocker rather than calling
    * this directly
    */
   static void AddScriptBlocker();
 
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -1050,17 +1050,17 @@ nsContentSink::ProcessOfflineManifest(co
     rv = mDocument->NodePrincipal()->CheckMayLoad(manifestURI, true, false);
     if (NS_FAILED(rv)) {
       action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
     }
     else {
       // 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::MaybeAllowOfflineAppByDefault(mDocument->NodePrincipal(), mDocument->GetWindow()) &&
           !nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal())) {
         return;
       }
 
       bool fetchedWithHTTPGetOrEquiv = false;
       nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mDocument->GetChannel()));
       if (httpChannel) {
         nsAutoCString method;
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -1426,45 +1426,41 @@ nsContentUtils::OfflineAppAllowed(nsIPri
   bool allowed;
   nsresult rv = updateService->OfflineAppAllowed(aPrincipal,
                                                  Preferences::GetRootBranch(),
                                                  &allowed);
   return NS_SUCCEEDED(rv) && allowed;
 }
 
 bool
-nsContentUtils::MaybeAllowOfflineAppByDefault(nsIPrincipal *aPrincipal)
+nsContentUtils::MaybeAllowOfflineAppByDefault(nsIPrincipal *aPrincipal,
+                                              nsIDOMWindow *aWindow)
 {
   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<nsIPermissionManager> permissionManager =
-      do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
-  if (!permissionManager)
+  nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
+    do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
+  if (!updateService) {
     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;
+  }
+
+  rv = updateService->AllowOfflineApp(aWindow, aPrincipal);
+  return NS_SUCCEEDED(rv);
 }
 
 // static
 void
 nsContentUtils::Shutdown()
 {
   sInitialized = false;
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -250,16 +250,23 @@ parent:
      *   cache group update. But we must cache the document (identified by the
      *   documentURI). This argument will ensure that a previously uncached 
      *   document will get cached and that we don't re-cache a document that 
      *   has already been cached (stickDocument=false).
      */
     POfflineCacheUpdate(URIParams manifestURI, URIParams documentURI,
                         bool stickDocument);
 
+    /**
+     * Sets "offline-app" permission for the principal.  Called when we hit
+     * a web app with the manifest attribute in <html> and
+     * offline-apps.allow_by_default is set to true.
+     */
+    SetOfflinePermission(Principal principal);
+
     sync PIndexedDB(nsCString group, nsCString asciiOrigin)
         returns (bool allowed);
 
     /**
      * window.open from inside <iframe mozbrowser> is special.  When the child
      * process calls window.open, it creates a new PBrowser (in its own
      * process), then calls BrowserFrameOpenWindow on it.
      *
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1573,16 +1573,24 @@ TabParent::DeallocPOfflineCacheUpdatePar
   mozilla::docshell::OfflineCacheUpdateParent* update =
     static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(actor);
 
   update->Release();
   return true;
 }
 
 bool
+TabParent::RecvSetOfflinePermission(const IPC::Principal& aPrincipal)
+{
+  nsIPrincipal* principal = aPrincipal;
+  nsContentUtils::MaybeAllowOfflineAppByDefault(principal, nullptr);
+  return true;
+}
+
+bool
 TabParent::ShouldDelayDialogs()
 {
   nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
   NS_ENSURE_TRUE(frameLoader, true);
   bool delay = false;
   frameLoader->GetDelayRemoteDialogs(&delay);
   return delay;
 }
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -232,16 +232,17 @@ public:
     AllocPContentPermissionRequestParent(const nsCString& aType, const nsCString& aAccess, const IPC::Principal& aPrincipal);
     virtual bool DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor);
 
     virtual POfflineCacheUpdateParent* AllocPOfflineCacheUpdateParent(
             const URIParams& aManifestURI,
             const URIParams& aDocumentURI,
             const bool& stickDocument) MOZ_OVERRIDE;
     virtual bool DeallocPOfflineCacheUpdateParent(POfflineCacheUpdateParent* actor);
+    virtual bool RecvSetOfflinePermission(const IPC::Principal& principal);
 
     bool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIAUTHPROMPTPROVIDER
     NS_DECL_NSISECUREBROWSERUI
 
     void HandleDelayedDialogs();
--- a/uriloader/prefetch/nsIOfflineCacheUpdate.idl
+++ b/uriloader/prefetch/nsIOfflineCacheUpdate.idl
@@ -192,17 +192,17 @@ interface nsIOfflineCacheUpdate : nsISup
   void cancel();
 
   /**
    * Return the number of bytes downloaded so far
    */
   readonly attribute uint64_t byteProgress;
 };
 
-[scriptable, uuid(cf362a31-4166-4994-8443-b68704ecdcc0)]
+[scriptable, uuid(6ee353ba-11ea-4008-a78a-55b343fb2a49)]
 interface nsIOfflineCacheUpdateService : nsISupports {
     /**
      * Constants for the offline-app permission.
      *
      * XXX: This isn't a great place for this, but it's really the only
      * private offline-app-related interface
      */
 
@@ -291,9 +291,16 @@ interface nsIOfflineCacheUpdateService :
      *        The URI to check
      * @param aPrefBranch
      *        The pref branch to use to check the
      *        offline-apps.allow_by_default pref.  If not specified,
      *        the pref service will be used.
      */
     boolean offlineAppAllowedForURI(in nsIURI aURI,
                                     in nsIPrefBranch aPrefBranch);
+
+    /**
+     * Sets the "offline-app" permission for the principal.
+     * In the single process model calls directly on permission manager.
+     * In the multi process model dispatches to the parent process.
+     */
+    void allowOfflineApp(in nsIDOMWindow aWindow, in nsIPrincipal aPrincipal);
 };
--- a/uriloader/prefetch/nsOfflineCacheUpdate.h
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.h
@@ -27,16 +27,18 @@
 #include "nsIURI.h"
 #include "nsIWebProgressListener.h"
 #include "nsClassHashtable.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWeakReference.h"
 #include "nsICryptoHash.h"
 #include "mozilla/Attributes.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
 
 class nsOfflineCacheUpdate;
 
 class nsICacheEntryDescriptor;
 class nsIUTF8StringEnumerator;
 class nsILoadContext;
 
 class nsOfflineCacheUpdateItem : public nsIDOMLoadStatus
@@ -345,19 +347,22 @@ public:
 
     /** Addrefs and returns the singleton nsOfflineCacheUpdateService. */
     static nsOfflineCacheUpdateService *GetInstance();
 
     static nsresult OfflineAppPinnedForURI(nsIURI *aDocumentURI,
                                            nsIPrefBranch *aPrefBranch,
                                            bool *aPinned);
 
+    static nsTHashtable<nsCStringHashKey>* AllowedDomains();
+
 private:
     nsresult ProcessNextUpdate();
 
     nsTArray<nsRefPtr<nsOfflineCacheUpdate> > mUpdates;
+    static nsTHashtable<nsCStringHashKey>* mAllowedDomains;
 
     bool mDisabled;
     bool mUpdateRunning;
     bool mLowFreeSpace;
 };
 
 #endif
--- a/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
@@ -43,21 +43,38 @@
 #include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 #include "prlog.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Attributes.h"
 #include "nsIDiskSpaceWatcher.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIDocShellTreeOwner.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
 static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nullptr;
 
+nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::mAllowedDomains = nullptr;
+
+nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::AllowedDomains()
+{
+    if (!mAllowedDomains)
+        mAllowedDomains = new nsTHashtable<nsCStringHashKey>();
+
+    return mAllowedDomains;
+}
+
+
 typedef mozilla::docshell::OfflineCacheUpdateParent OfflineCacheUpdateParent;
 typedef mozilla::docshell::OfflineCacheUpdateChild OfflineCacheUpdateChild;
 typedef mozilla::docshell::OfflineCacheUpdateGlue OfflineCacheUpdateGlue;
 
 #if defined(PR_LOGGING)
 //
 // To enable logging (see prlog.h for full details):
 //
@@ -676,16 +693,25 @@ OfflineAppPermForURI(nsIURI *aURI,
     if (!match) {
         rv = innerURI->SchemeIs("https", &match);
         NS_ENSURE_SUCCESS(rv, rv);
         if (!match) {
             return NS_OK;
         }
     }
 
+    nsAutoCString domain;
+    rv = innerURI->GetAsciiHost(domain);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (nsOfflineCacheUpdateService::AllowedDomains()->Contains(domain)) {
+        *aAllowed = true;
+        return NS_OK;
+    }
+
     nsCOMPtr<nsIPermissionManager> permissionManager =
         do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
     if (!permissionManager) {
         return NS_OK;
     }
 
     uint32_t perm;
     const char *permName = pinned ? "pin-app" : "offline-app";
@@ -712,8 +738,44 @@ nsOfflineCacheUpdateService::OfflineAppA
 
 nsresult
 nsOfflineCacheUpdateService::OfflineAppPinnedForURI(nsIURI *aDocumentURI,
                                                     nsIPrefBranch *aPrefBranch,
                                                     bool *aPinned)
 {
     return OfflineAppPermForURI(aDocumentURI, aPrefBranch, true, aPinned);
 }
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::AllowOfflineApp(nsIDOMWindow *aWindow,
+                                             nsIPrincipal *aPrincipal)
+{
+    nsresult rv;
+
+    if (GeckoProcessType_Default != XRE_GetProcessType()) {
+        TabChild* child = TabChild::GetFrom(aWindow);
+        NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
+
+        if (!child->SendSetOfflinePermission(IPC::Principal(aPrincipal))) {
+            return NS_ERROR_FAILURE;
+        }
+
+        nsAutoCString domain;
+        rv = aPrincipal->GetBaseDomain(domain);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        nsOfflineCacheUpdateService::AllowedDomains()->PutEntry(domain);
+    }
+    else {
+        nsCOMPtr<nsIPermissionManager> permissionManager =
+            do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+        if (!permissionManager)
+            return NS_ERROR_NOT_AVAILABLE;
+
+        rv = permissionManager->AddFromPrincipal(
+            aPrincipal, "offline-app", nsIPermissionManager::ALLOW_ACTION,
+            nsIPermissionManager::EXPIRE_NEVER, 0);
+        if (NS_FAILED(rv))
+            return rv;
+    }
+
+    return NS_OK;
+}