Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include <stdlib.h> |
michael@0 | 7 | #include <stdarg.h> |
michael@0 | 8 | |
michael@0 | 9 | #include "prprf.h" |
michael@0 | 10 | |
michael@0 | 11 | #include "nsIServiceManager.h" |
michael@0 | 12 | |
michael@0 | 13 | #include "nsIConsoleService.h" |
michael@0 | 14 | #include "nsIDOMCanvasRenderingContext2D.h" |
michael@0 | 15 | #include "nsICanvasRenderingContextInternal.h" |
michael@0 | 16 | #include "nsIHTMLCollection.h" |
michael@0 | 17 | #include "mozilla/dom/HTMLCanvasElement.h" |
michael@0 | 18 | #include "nsIPrincipal.h" |
michael@0 | 19 | |
michael@0 | 20 | #include "nsGfxCIID.h" |
michael@0 | 21 | |
michael@0 | 22 | #include "nsTArray.h" |
michael@0 | 23 | |
michael@0 | 24 | #include "CanvasUtils.h" |
michael@0 | 25 | #include "mozilla/gfx/Matrix.h" |
michael@0 | 26 | |
michael@0 | 27 | using namespace mozilla::gfx; |
michael@0 | 28 | |
michael@0 | 29 | #include "nsIScriptObjectPrincipal.h" |
michael@0 | 30 | #include "nsIPermissionManager.h" |
michael@0 | 31 | #include "nsIObserverService.h" |
michael@0 | 32 | #include "mozilla/Services.h" |
michael@0 | 33 | #include "mozIThirdPartyUtil.h" |
michael@0 | 34 | #include "nsContentUtils.h" |
michael@0 | 35 | #include "nsUnicharUtils.h" |
michael@0 | 36 | #include "nsPrintfCString.h" |
michael@0 | 37 | #include "nsIConsoleService.h" |
michael@0 | 38 | #include "jsapi.h" |
michael@0 | 39 | |
michael@0 | 40 | #define TOPIC_CANVAS_PERMISSIONS_PROMPT "canvas-permissions-prompt" |
michael@0 | 41 | #define PERMISSION_CANVAS_EXTRACT_DATA "canvas/extractData" |
michael@0 | 42 | |
michael@0 | 43 | namespace mozilla { |
michael@0 | 44 | namespace CanvasUtils { |
michael@0 | 45 | |
michael@0 | 46 | // Check site-specific permission and display prompt if appropriate. |
michael@0 | 47 | bool IsImageExtractionAllowed(nsIDocument *aDocument, JSContext *aCx) |
michael@0 | 48 | { |
michael@0 | 49 | if (!aDocument || !aCx) |
michael@0 | 50 | return false; |
michael@0 | 51 | |
michael@0 | 52 | nsPIDOMWindow *win = aDocument->GetWindow(); |
michael@0 | 53 | nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(win)); |
michael@0 | 54 | if (sop && nsContentUtils::IsSystemPrincipal(sop->GetPrincipal())) |
michael@0 | 55 | return true; |
michael@0 | 56 | |
michael@0 | 57 | // Don't show canvas prompt for chrome scripts (e.g. Page Inspector) |
michael@0 | 58 | if (nsContentUtils::IsCallerChrome()) |
michael@0 | 59 | return true; |
michael@0 | 60 | |
michael@0 | 61 | JS::AutoFilename scriptFile; |
michael@0 | 62 | unsigned scriptLine = 0; |
michael@0 | 63 | bool isScriptKnown = false; |
michael@0 | 64 | if (JS::DescribeScriptedCaller(aCx, &scriptFile, &scriptLine)) { |
michael@0 | 65 | isScriptKnown = true; |
michael@0 | 66 | // Don't show canvas prompt for PDF.js |
michael@0 | 67 | if (scriptFile.get() && |
michael@0 | 68 | strcmp(scriptFile.get(), "resource://pdf.js/build/pdf.js") == 0) |
michael@0 | 69 | return true; |
michael@0 | 70 | } |
michael@0 | 71 | bool isAllowed = false; |
michael@0 | 72 | nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = |
michael@0 | 73 | do_GetService(THIRDPARTYUTIL_CONTRACTID); |
michael@0 | 74 | nsCOMPtr<nsIPermissionManager> permissionManager = |
michael@0 | 75 | do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); |
michael@0 | 76 | if (thirdPartyUtil && permissionManager) { |
michael@0 | 77 | nsCOMPtr<nsIURI> uri; |
michael@0 | 78 | nsresult rv = thirdPartyUtil->GetFirstPartyURI(NULL, aDocument, |
michael@0 | 79 | getter_AddRefs(uri)); |
michael@0 | 80 | uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION; |
michael@0 | 81 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 82 | // Allow local files to access canvas data; check content permissions |
michael@0 | 83 | // for remote pages. |
michael@0 | 84 | bool isFileURL = false; |
michael@0 | 85 | (void)uri->SchemeIs("file", &isFileURL); |
michael@0 | 86 | if (isFileURL) |
michael@0 | 87 | permission = nsIPermissionManager::ALLOW_ACTION; |
michael@0 | 88 | else { |
michael@0 | 89 | rv = permissionManager->TestPermission(uri, |
michael@0 | 90 | PERMISSION_CANVAS_EXTRACT_DATA, &permission); |
michael@0 | 91 | } |
michael@0 | 92 | } |
michael@0 | 93 | |
michael@0 | 94 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 95 | isAllowed = (permission == nsIPermissionManager::ALLOW_ACTION); |
michael@0 | 96 | |
michael@0 | 97 | if (!isAllowed && (permission != nsIPermissionManager::DENY_ACTION)) { |
michael@0 | 98 | // Log all attempted canvas access and block access by third parties. |
michael@0 | 99 | bool isThirdParty = true; |
michael@0 | 100 | nsIURI *docURI = aDocument->GetDocumentURI(); |
michael@0 | 101 | rv = thirdPartyUtil->IsThirdPartyURI(uri, docURI, &isThirdParty); |
michael@0 | 102 | NS_ENSURE_SUCCESS(rv, false); |
michael@0 | 103 | |
michael@0 | 104 | nsCString firstPartySpec; |
michael@0 | 105 | rv = uri->GetSpec(firstPartySpec); |
michael@0 | 106 | nsCString docSpec; |
michael@0 | 107 | docURI->GetSpec(docSpec); |
michael@0 | 108 | nsPrintfCString msg("On %s: blocked access to canvas image data" |
michael@0 | 109 | " from document %s, ", // L10n |
michael@0 | 110 | firstPartySpec.get(), docSpec.get()); |
michael@0 | 111 | if (isScriptKnown && scriptFile.get()) { |
michael@0 | 112 | msg += nsPrintfCString("script from %s:%u", // L10n |
michael@0 | 113 | scriptFile.get(), scriptLine); |
michael@0 | 114 | } else { |
michael@0 | 115 | msg += nsPrintfCString("unknown script"); // L10n |
michael@0 | 116 | } |
michael@0 | 117 | nsCOMPtr<nsIConsoleService> console |
michael@0 | 118 | (do_GetService(NS_CONSOLESERVICE_CONTRACTID)); |
michael@0 | 119 | if (console) |
michael@0 | 120 | console->LogStringMessage(NS_ConvertUTF8toUTF16(msg).get()); |
michael@0 | 121 | |
michael@0 | 122 | // Log every canvas access attempt to stdout if debugging: |
michael@0 | 123 | #ifdef DEBUG |
michael@0 | 124 | printf("%s\n", msg.get()); |
michael@0 | 125 | #endif |
michael@0 | 126 | // Ensure URI is valid after logging, but before trying to notify the |
michael@0 | 127 | // user: |
michael@0 | 128 | NS_ENSURE_SUCCESS(rv, false); |
michael@0 | 129 | |
michael@0 | 130 | if (!isThirdParty) { |
michael@0 | 131 | // Send notification so that a prompt is displayed. |
michael@0 | 132 | nsCOMPtr<nsIObserverService> obs = |
michael@0 | 133 | mozilla::services::GetObserverService(); |
michael@0 | 134 | obs->NotifyObservers(win, TOPIC_CANVAS_PERMISSIONS_PROMPT, |
michael@0 | 135 | NS_ConvertUTF8toUTF16(firstPartySpec).get()); |
michael@0 | 136 | } |
michael@0 | 137 | } |
michael@0 | 138 | } |
michael@0 | 139 | } |
michael@0 | 140 | |
michael@0 | 141 | return isAllowed; |
michael@0 | 142 | } |
michael@0 | 143 | |
michael@0 | 144 | void |
michael@0 | 145 | DoDrawImageSecurityCheck(dom::HTMLCanvasElement *aCanvasElement, |
michael@0 | 146 | nsIPrincipal *aPrincipal, |
michael@0 | 147 | bool forceWriteOnly, |
michael@0 | 148 | bool CORSUsed) |
michael@0 | 149 | { |
michael@0 | 150 | NS_PRECONDITION(aPrincipal, "Must have a principal here"); |
michael@0 | 151 | |
michael@0 | 152 | // Callers should ensure that mCanvasElement is non-null before calling this |
michael@0 | 153 | if (!aCanvasElement) { |
michael@0 | 154 | NS_WARNING("DoDrawImageSecurityCheck called without canvas element!"); |
michael@0 | 155 | return; |
michael@0 | 156 | } |
michael@0 | 157 | |
michael@0 | 158 | if (aCanvasElement->IsWriteOnly()) |
michael@0 | 159 | return; |
michael@0 | 160 | |
michael@0 | 161 | // If we explicitly set WriteOnly just do it and get out |
michael@0 | 162 | if (forceWriteOnly) { |
michael@0 | 163 | aCanvasElement->SetWriteOnly(); |
michael@0 | 164 | return; |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | // No need to do a security check if the image used CORS for the load |
michael@0 | 168 | if (CORSUsed) |
michael@0 | 169 | return; |
michael@0 | 170 | |
michael@0 | 171 | if (aCanvasElement->NodePrincipal()->Subsumes(aPrincipal)) { |
michael@0 | 172 | // This canvas has access to that image anyway |
michael@0 | 173 | return; |
michael@0 | 174 | } |
michael@0 | 175 | |
michael@0 | 176 | aCanvasElement->SetWriteOnly(); |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | bool |
michael@0 | 180 | CoerceDouble(JS::Value v, double* d) |
michael@0 | 181 | { |
michael@0 | 182 | if (JSVAL_IS_DOUBLE(v)) { |
michael@0 | 183 | *d = JSVAL_TO_DOUBLE(v); |
michael@0 | 184 | } else if (JSVAL_IS_INT(v)) { |
michael@0 | 185 | *d = double(JSVAL_TO_INT(v)); |
michael@0 | 186 | } else if (JSVAL_IS_VOID(v)) { |
michael@0 | 187 | *d = 0.0; |
michael@0 | 188 | } else { |
michael@0 | 189 | return false; |
michael@0 | 190 | } |
michael@0 | 191 | return true; |
michael@0 | 192 | } |
michael@0 | 193 | |
michael@0 | 194 | } // namespace CanvasUtils |
michael@0 | 195 | } // namespace mozilla |