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