Bug 1305339 and bug 1310518 - improve downloads quarantine support and fix crashes, r=mstange,bz,a=gchang
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Fri, 28 Oct 2016 17:38:29 +0100
changeset 340853 7f6f97d57ad5764ecf161567ca45baf835894b4f
parent 340852 45118388b658d19e06fe553cc41654eca677c64c
child 340854 559084cb05017e8d3abb4dc6191e42fa1a21fe41
push id10235
push userkwierso@gmail.com
push dateTue, 01 Nov 2016 17:29:49 +0000
treeherdermozilla-aurora@3c4ab75a8bcb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange, bz, gchang
bugs1305339, 1310518
milestone51.0a2
Bug 1305339 and bug 1310518 - improve downloads quarantine support and fix crashes, r=mstange,bz,a=gchang
browser/app/macbuild/Contents/Info.plist.in
toolkit/components/jsdownloads/src/DownloadPlatform.cpp
toolkit/components/jsdownloads/src/DownloadPlatform.h
xpcom/io/CocoaFileUtils.h
xpcom/io/CocoaFileUtils.mm
--- a/browser/app/macbuild/Contents/Info.plist.in
+++ b/browser/app/macbuild/Contents/Info.plist.in
@@ -200,16 +200,18 @@
 		</dict>
 	</array>
 	<key>CFBundleVersion</key>
 	<string>%MAC_BUNDLE_VERSION%</string>
 	<key>NSAppleScriptEnabled</key>
 	<true/>
 	<key>LSApplicationCategoryType</key>
 	<string>public.app-category.productivity</string>
+	<key>LSFileQuarantineEnabled</key>
+	<true/>
 	<key>LSMinimumSystemVersion</key>
 	<string>10.9.0</string>
   <key>NSSupportsAutomaticGraphicsSwitching</key>
   <true/>
   <key>NSPrincipalClass</key>
   <string>GeckoNSApplication</string>
 	<key>SMPrivilegedExecutables</key>
 	<dict>
--- a/toolkit/components/jsdownloads/src/DownloadPlatform.cpp
+++ b/toolkit/components/jsdownloads/src/DownloadPlatform.cpp
@@ -1,15 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DownloadPlatform.h"
 #include "nsAutoPtr.h"
+#include "nsNetUtil.h"
 #include "nsString.h"
+#include "nsINestedURI.h"
+#include "nsIProtocolHandler.h"
 #include "nsIURI.h"
 #include "nsIFile.h"
 #include "nsIObserverService.h"
 #include "nsISupportsPrimitives.h"
 #include "nsDirectoryServiceDefs.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
@@ -160,25 +163,28 @@ nsresult DownloadPlatform::DownloadDone(
     // Add OS X origin and referrer file metadata
     CFStringRef pathCFStr = NULL;
     if (!path.IsEmpty()) {
       pathCFStr = ::CFStringCreateWithCharacters(kCFAllocatorDefault,
                                                  (const UniChar*)path.get(),
                                                  path.Length());
     }
     if (pathCFStr) {
+      bool isFromWeb = IsURLPossiblyFromWeb(aSource);
+
       CFURLRef sourceCFURL = CreateCFURLFromNSIURI(aSource);
       CFURLRef referrerCFURL = CreateCFURLFromNSIURI(aReferrer);
 
       CocoaFileUtils::AddOriginMetadataToFile(pathCFStr,
                                               sourceCFURL,
                                               referrerCFURL);
       CocoaFileUtils::AddQuarantineMetadataToFile(pathCFStr,
                                                   sourceCFURL,
-                                                  referrerCFURL);
+                                                  referrerCFURL,
+                                                  isFromWeb);
 
       ::CFRelease(pathCFStr);
       if (sourceCFURL) {
         ::CFRelease(sourceCFURL);
       }
       if (referrerCFURL) {
         ::CFRelease(referrerCFURL);
       }
@@ -222,8 +228,60 @@ nsresult DownloadPlatform::MapUrlToZone(
     *aZone = zone;
   }
 
   return NS_OK;
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
+
+// Check if a URI is likely to be web-based, by checking its URI flags.
+// If in doubt (e.g. if anything fails during the check) claims things
+// are from the web.
+bool DownloadPlatform::IsURLPossiblyFromWeb(nsIURI* aURI)
+{
+  nsCOMPtr<nsIIOService> ios = do_GetIOService();
+  nsCOMPtr<nsIURI> uri = aURI;
+  if (!ios) {
+    return true;
+  }
+
+  while (uri) {
+    // We're not using nsIIOService::ProtocolHasFlags because it doesn't
+    // take per-URI flags into account. We're also not using
+    // NS_URIChainHasFlags because we're checking for *any* of 3 flags
+    // to be present on *all* of the nested URIs, which it can't do.
+    nsAutoCString scheme;
+    nsresult rv = uri->GetScheme(scheme);
+    if (NS_FAILED(rv)) {
+      return true;
+    }
+    nsCOMPtr<nsIProtocolHandler> ph;
+    rv = ios->GetProtocolHandler(scheme.get(), getter_AddRefs(ph));
+    if (NS_FAILED(rv)) {
+      return true;
+    }
+    uint32_t flags;
+    rv = ph->DoGetProtocolFlags(uri, &flags);
+    if (NS_FAILED(rv)) {
+      return true;
+    }
+    // If not dangerous to load, not a UI resource and not a local file,
+    // assume this is from the web:
+    if (!(flags & nsIProtocolHandler::URI_DANGEROUS_TO_LOAD) &&
+        !(flags & nsIProtocolHandler::URI_IS_UI_RESOURCE) &&
+        !(flags & nsIProtocolHandler::URI_IS_LOCAL_FILE)) {
+      return true;
+    }
+    // Otherwise, check if the URI is nested, and if so go through
+    // the loop again:
+    nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(uri);
+    uri = nullptr;
+    if (nestedURI) {
+      rv = nestedURI->GetInnerURI(getter_AddRefs(uri));
+      if (NS_FAILED(rv)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
--- a/toolkit/components/jsdownloads/src/DownloadPlatform.h
+++ b/toolkit/components/jsdownloads/src/DownloadPlatform.h
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __DownloadPlatform_h__
 #define __DownloadPlatform_h__
 
 #include "mozIDownloadPlatform.h"
 
 #include "nsCOMPtr.h"
+class nsIURI;
 
 class DownloadPlatform : public mozIDownloadPlatform
 {
 protected:
 
   virtual ~DownloadPlatform() { }
 
 public:
@@ -20,11 +21,14 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_MOZIDOWNLOADPLATFORM
 
   DownloadPlatform() { }
 
   static DownloadPlatform *gDownloadPlatformService;
 
   static DownloadPlatform* GetDownloadPlatform();
+
+private:
+  static bool IsURLPossiblyFromWeb(nsIURI* aURI);
 };
 
 #endif
--- a/xpcom/io/CocoaFileUtils.h
+++ b/xpcom/io/CocoaFileUtils.h
@@ -21,13 +21,14 @@ nsresult GetFileCreatorCode(CFURLRef aUr
 nsresult SetFileCreatorCode(CFURLRef aUrl, OSType aCreatorCode);
 nsresult GetFileTypeCode(CFURLRef aUrl, OSType* aTypeCode);
 nsresult SetFileTypeCode(CFURLRef aUrl, OSType aTypeCode);
 void     AddOriginMetadataToFile(const CFStringRef filePath,
                                  const CFURLRef sourceURL,
                                  const CFURLRef referrerURL);
 void     AddQuarantineMetadataToFile(const CFStringRef filePath,
                                      const CFURLRef sourceURL,
-                                     const CFURLRef referrerURL);
+                                     const CFURLRef referrerURL,
+                                     const bool isFromWeb);
 
 } // namespace CocoaFileUtils
 
 #endif
--- a/xpcom/io/CocoaFileUtils.mm
+++ b/xpcom/io/CocoaFileUtils.mm
@@ -1,20 +1,26 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 // vim:set ts=2 sts=2 sw=2 et cin:
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CocoaFileUtils.h"
+#include "nsCocoaFeatures.h"
 #include "nsCocoaUtils.h"
 #include <Cocoa/Cocoa.h>
 #include "nsObjCExceptions.h"
 #include "nsDebug.h"
 
+// Need to cope with us using old versions of the SDK and needing this on 10.10+
+#if !defined(MAC_OS_X_VERSION_10_10) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10)
+const CFStringRef kCFURLQuarantinePropertiesKey = CFSTR("NSURLQuarantinePropertiesKey");
+#endif
+
 namespace CocoaFileUtils {
 
 nsresult RevealFileInFinder(CFURLRef url)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   if (NS_WARN_IF(!url))
     return NS_ERROR_INVALID_ARG;
@@ -182,25 +188,33 @@ void AddOriginMetadataToFile(const CFStr
   mdItemSetAttributeFunc(mdItem, kMDItemWhereFroms, list);
 
   ::CFRelease(list);
   ::CFRelease(mdItem);
 }
 
 void AddQuarantineMetadataToFile(const CFStringRef filePath,
                                  const CFURLRef sourceURL,
-                                 const CFURLRef referrerURL) {
+                                 const CFURLRef referrerURL,
+                                 const bool isFromWeb) {
   CFURLRef fileURL = ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
                                                      filePath,
                                                      kCFURLPOSIXPathStyle,
                                                      false);
 
+  // The properties key changed in 10.10:
+  CFStringRef quarantinePropKey;
+  if (nsCocoaFeatures::OnYosemiteOrLater()) {
+    quarantinePropKey = kCFURLQuarantinePropertiesKey;
+  } else {
+    quarantinePropKey = kLSItemQuarantineProperties;
+  }
   CFDictionaryRef quarantineProps = NULL;
   Boolean success = ::CFURLCopyResourcePropertyForKey(fileURL,
-                                                      kLSItemQuarantineProperties,
+                                                      quarantinePropKey,
                                                       &quarantineProps,
                                                       NULL);
 
   // If there aren't any quarantine properties then the user probably
   // set up an exclusion and we don't need to add metadata.
   if (!success || !quarantineProps) {
     ::CFRelease(fileURL);
     return;
@@ -216,39 +230,31 @@ void AddQuarantineMetadataToFile(const C
   // Make a mutable copy of the properties.
   CFMutableDictionaryRef mutQuarantineProps =
     ::CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, (CFDictionaryRef)quarantineProps);
   ::CFRelease(quarantineProps);
 
   // Add metadata that the OS couldn't infer.
 
   if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineTypeKey)) {
-    CFStringRef type = kLSQuarantineTypeOtherDownload;
-
-    CFStringRef scheme = ::CFURLCopyScheme(sourceURL);
-    if (::CFStringCompare(scheme, CFSTR("http"), kCFCompareCaseInsensitive) == kCFCompareEqualTo ||
-        ::CFStringCompare(scheme, CFSTR("http"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
-      type = kLSQuarantineTypeWebDownload;
-    }
-    ::CFRelease(scheme);
-
+    CFStringRef type = isFromWeb ? kLSQuarantineTypeWebDownload : kLSQuarantineTypeOtherDownload;
     ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineTypeKey, type);
   }
 
-  if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineOriginURLKey)) {
+  if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineOriginURLKey) && referrerURL) {
     ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineOriginURLKey, referrerURL);
   }
 
-  if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineDataURLKey)) {
+  if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineDataURLKey) && sourceURL) {
     ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineDataURLKey, sourceURL);
   }
 
   // Set quarantine properties on file.
   ::CFURLSetResourcePropertyForKey(fileURL,
-                                   kLSItemQuarantineProperties,
+                                   quarantinePropKey,
                                    mutQuarantineProps,
                                    NULL);
 
   ::CFRelease(fileURL);
   ::CFRelease(mutQuarantineProps);
 }
 
 } // namespace CocoaFileUtils