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