content/canvas/src/CanvasUtils.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/canvas/src/CanvasUtils.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,195 @@
     1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include <stdlib.h>
    1.10 +#include <stdarg.h>
    1.11 +
    1.12 +#include "prprf.h"
    1.13 +
    1.14 +#include "nsIServiceManager.h"
    1.15 +
    1.16 +#include "nsIConsoleService.h"
    1.17 +#include "nsIDOMCanvasRenderingContext2D.h"
    1.18 +#include "nsICanvasRenderingContextInternal.h"
    1.19 +#include "nsIHTMLCollection.h"
    1.20 +#include "mozilla/dom/HTMLCanvasElement.h"
    1.21 +#include "nsIPrincipal.h"
    1.22 +
    1.23 +#include "nsGfxCIID.h"
    1.24 +
    1.25 +#include "nsTArray.h"
    1.26 +
    1.27 +#include "CanvasUtils.h"
    1.28 +#include "mozilla/gfx/Matrix.h"
    1.29 +
    1.30 +using namespace mozilla::gfx;
    1.31 +
    1.32 +#include "nsIScriptObjectPrincipal.h"
    1.33 +#include "nsIPermissionManager.h"
    1.34 +#include "nsIObserverService.h"
    1.35 +#include "mozilla/Services.h"
    1.36 +#include "mozIThirdPartyUtil.h"
    1.37 +#include "nsContentUtils.h"
    1.38 +#include "nsUnicharUtils.h"
    1.39 +#include "nsPrintfCString.h"
    1.40 +#include "nsIConsoleService.h"
    1.41 +#include "jsapi.h"
    1.42 +
    1.43 +#define TOPIC_CANVAS_PERMISSIONS_PROMPT "canvas-permissions-prompt"
    1.44 +#define PERMISSION_CANVAS_EXTRACT_DATA "canvas/extractData"
    1.45 +
    1.46 +namespace mozilla {
    1.47 +namespace CanvasUtils {
    1.48 +
    1.49 +// Check site-specific permission and display prompt if appropriate.
    1.50 +bool IsImageExtractionAllowed(nsIDocument *aDocument, JSContext *aCx)
    1.51 +{
    1.52 +  if (!aDocument || !aCx)
    1.53 +    return false;
    1.54 +
    1.55 +  nsPIDOMWindow *win = aDocument->GetWindow();
    1.56 +  nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(win));
    1.57 +  if (sop && nsContentUtils::IsSystemPrincipal(sop->GetPrincipal()))
    1.58 +    return true;
    1.59 +
    1.60 +  // Don't show canvas prompt for chrome scripts (e.g. Page Inspector)
    1.61 +  if (nsContentUtils::IsCallerChrome())
    1.62 +    return true;
    1.63 +
    1.64 +  JS::AutoFilename scriptFile;
    1.65 +  unsigned scriptLine = 0;
    1.66 +  bool isScriptKnown = false;
    1.67 +  if (JS::DescribeScriptedCaller(aCx, &scriptFile, &scriptLine)) {
    1.68 +    isScriptKnown = true;
    1.69 +    // Don't show canvas prompt for PDF.js
    1.70 +    if (scriptFile.get() &&
    1.71 +        strcmp(scriptFile.get(), "resource://pdf.js/build/pdf.js") == 0)
    1.72 +      return true;
    1.73 +  }
    1.74 +  bool isAllowed = false;
    1.75 +  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
    1.76 +                                do_GetService(THIRDPARTYUTIL_CONTRACTID);
    1.77 +  nsCOMPtr<nsIPermissionManager> permissionManager =
    1.78 +                          do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
    1.79 +  if (thirdPartyUtil && permissionManager) {
    1.80 +    nsCOMPtr<nsIURI> uri;
    1.81 +    nsresult rv = thirdPartyUtil->GetFirstPartyURI(NULL, aDocument,
    1.82 +                                                   getter_AddRefs(uri));
    1.83 +    uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
    1.84 +    if (NS_SUCCEEDED(rv)) {
    1.85 +      // Allow local files to access canvas data; check content permissions
    1.86 +      // for remote pages.
    1.87 +      bool isFileURL = false;
    1.88 +      (void)uri->SchemeIs("file", &isFileURL);
    1.89 +      if (isFileURL)
    1.90 +        permission = nsIPermissionManager::ALLOW_ACTION;
    1.91 +      else {
    1.92 +        rv = permissionManager->TestPermission(uri,
    1.93 +                                PERMISSION_CANVAS_EXTRACT_DATA, &permission);
    1.94 +      }
    1.95 +    }
    1.96 +
    1.97 +    if (NS_SUCCEEDED(rv)) {
    1.98 +      isAllowed = (permission == nsIPermissionManager::ALLOW_ACTION);
    1.99 +
   1.100 +      if (!isAllowed && (permission != nsIPermissionManager::DENY_ACTION)) {
   1.101 +        // Log all attempted canvas access and block access by third parties.
   1.102 +        bool isThirdParty = true;
   1.103 +        nsIURI *docURI = aDocument->GetDocumentURI();
   1.104 +        rv = thirdPartyUtil->IsThirdPartyURI(uri, docURI, &isThirdParty);
   1.105 +        NS_ENSURE_SUCCESS(rv, false);
   1.106 +
   1.107 +        nsCString firstPartySpec;
   1.108 +        rv = uri->GetSpec(firstPartySpec);
   1.109 +        nsCString docSpec;
   1.110 +        docURI->GetSpec(docSpec);
   1.111 +        nsPrintfCString msg("On %s: blocked access to canvas image data"
   1.112 +                            " from document %s, ",  // L10n
   1.113 +                            firstPartySpec.get(), docSpec.get());
   1.114 +        if (isScriptKnown && scriptFile.get()) {
   1.115 +          msg += nsPrintfCString("script from %s:%u",  // L10n
   1.116 +                                 scriptFile.get(), scriptLine);
   1.117 +        } else {
   1.118 +          msg += nsPrintfCString("unknown script");  // L10n
   1.119 +        }
   1.120 +        nsCOMPtr<nsIConsoleService> console
   1.121 +                              (do_GetService(NS_CONSOLESERVICE_CONTRACTID));
   1.122 +        if (console)
   1.123 +          console->LogStringMessage(NS_ConvertUTF8toUTF16(msg).get());
   1.124 +
   1.125 +        // Log every canvas access attempt to stdout if debugging:
   1.126 +#ifdef DEBUG
   1.127 +        printf("%s\n", msg.get());
   1.128 +#endif
   1.129 +        // Ensure URI is valid after logging, but before trying to notify the
   1.130 +        // user:
   1.131 +        NS_ENSURE_SUCCESS(rv, false);
   1.132 +
   1.133 +        if (!isThirdParty) {
   1.134 +          // Send notification so that a prompt is displayed.
   1.135 +          nsCOMPtr<nsIObserverService> obs =
   1.136 +                                       mozilla::services::GetObserverService();
   1.137 +          obs->NotifyObservers(win, TOPIC_CANVAS_PERMISSIONS_PROMPT,
   1.138 +                               NS_ConvertUTF8toUTF16(firstPartySpec).get());
   1.139 +        }
   1.140 +      }
   1.141 +    }
   1.142 +  }
   1.143 +
   1.144 +  return isAllowed;
   1.145 +}
   1.146 +
   1.147 +void
   1.148 +DoDrawImageSecurityCheck(dom::HTMLCanvasElement *aCanvasElement,
   1.149 +                         nsIPrincipal *aPrincipal,
   1.150 +                         bool forceWriteOnly,
   1.151 +                         bool CORSUsed)
   1.152 +{
   1.153 +    NS_PRECONDITION(aPrincipal, "Must have a principal here");
   1.154 +
   1.155 +    // Callers should ensure that mCanvasElement is non-null before calling this
   1.156 +    if (!aCanvasElement) {
   1.157 +        NS_WARNING("DoDrawImageSecurityCheck called without canvas element!");
   1.158 +        return;
   1.159 +    }
   1.160 +
   1.161 +    if (aCanvasElement->IsWriteOnly())
   1.162 +        return;
   1.163 +
   1.164 +    // If we explicitly set WriteOnly just do it and get out
   1.165 +    if (forceWriteOnly) {
   1.166 +        aCanvasElement->SetWriteOnly();
   1.167 +        return;
   1.168 +    }
   1.169 +
   1.170 +    // No need to do a security check if the image used CORS for the load
   1.171 +    if (CORSUsed)
   1.172 +        return;
   1.173 +
   1.174 +    if (aCanvasElement->NodePrincipal()->Subsumes(aPrincipal)) {
   1.175 +        // This canvas has access to that image anyway
   1.176 +        return;
   1.177 +    }
   1.178 +
   1.179 +    aCanvasElement->SetWriteOnly();
   1.180 +}
   1.181 +
   1.182 +bool
   1.183 +CoerceDouble(JS::Value v, double* d)
   1.184 +{
   1.185 +    if (JSVAL_IS_DOUBLE(v)) {
   1.186 +        *d = JSVAL_TO_DOUBLE(v);
   1.187 +    } else if (JSVAL_IS_INT(v)) {
   1.188 +        *d = double(JSVAL_TO_INT(v));
   1.189 +    } else if (JSVAL_IS_VOID(v)) {
   1.190 +        *d = 0.0;
   1.191 +    } else {
   1.192 +        return false;
   1.193 +    }
   1.194 +    return true;
   1.195 +}
   1.196 +
   1.197 +} // namespace CanvasUtils
   1.198 +} // namespace mozilla

mercurial