Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/canvas/CanvasUtils.cpp
Line
Count
Source (jump to first uncovered line)
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 "nsIServiceManager.h"
10
11
#include "nsIConsoleService.h"
12
#include "nsICanvasRenderingContextInternal.h"
13
#include "nsIHTMLCollection.h"
14
#include "mozilla/dom/HTMLCanvasElement.h"
15
#include "mozilla/dom/TabChild.h"
16
#include "mozilla/EventStateManager.h"
17
#include "mozilla/StaticPrefs.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
#include "WebGL2Context.h"
27
28
#include "nsIScriptObjectPrincipal.h"
29
#include "nsIPermissionManager.h"
30
#include "nsIObserverService.h"
31
#include "mozilla/Services.h"
32
#include "mozIThirdPartyUtil.h"
33
#include "nsContentUtils.h"
34
#include "nsUnicharUtils.h"
35
#include "nsPrintfCString.h"
36
#include "nsIConsoleService.h"
37
#include "jsapi.h"
38
39
0
#define TOPIC_CANVAS_PERMISSIONS_PROMPT "canvas-permissions-prompt"
40
0
#define PERMISSION_CANVAS_EXTRACT_DATA "canvas"
41
42
using namespace mozilla::gfx;
43
44
namespace mozilla {
45
namespace CanvasUtils {
46
47
bool IsImageExtractionAllowed(nsIDocument *aDocument, JSContext *aCx, nsIPrincipal& aPrincipal)
48
0
{
49
0
    // Do the rest of the checks only if privacy.resistFingerprinting is on.
50
0
    if (!nsContentUtils::ShouldResistFingerprinting()) {
51
0
        return true;
52
0
    }
53
0
54
0
    // Don't proceed if we don't have a document or JavaScript context.
55
0
    if (!aDocument || !aCx) {
56
0
        return false;
57
0
    }
58
0
59
0
    // The system principal can always extract canvas data.
60
0
    if (nsContentUtils::IsSystemPrincipal(&aPrincipal)) {
61
0
        return true;
62
0
    }
63
0
64
0
    // Allow extension principals.
65
0
    auto principal = BasePrincipal::Cast(&aPrincipal);
66
0
    if (principal->AddonPolicy() || principal->ContentScriptAddonPolicy()) {
67
0
        return true;
68
0
    }
69
0
70
0
    // Get the document URI and its spec.
71
0
    nsIURI *docURI = aDocument->GetDocumentURI();
72
0
    nsCString docURISpec;
73
0
    docURI->GetSpec(docURISpec);
74
0
75
0
    // Allow local files to extract canvas data.
76
0
    bool isFileURL;
77
0
    if (NS_SUCCEEDED(docURI->SchemeIs("file", &isFileURL)) && isFileURL) {
78
0
        return true;
79
0
    }
80
0
81
0
    // Get calling script file and line for logging.
82
0
    JS::AutoFilename scriptFile;
83
0
    unsigned scriptLine = 0;
84
0
    bool isScriptKnown = false;
85
0
    if (JS::DescribeScriptedCaller(aCx, &scriptFile, &scriptLine)) {
86
0
        isScriptKnown = true;
87
0
        // Don't show canvas prompt for PDF.js
88
0
        if (scriptFile.get() &&
89
0
                strcmp(scriptFile.get(), "resource://pdf.js/build/pdf.js") == 0) {
90
0
            return true;
91
0
        }
92
0
    }
93
0
94
0
    nsIDocument* topLevelDocument = aDocument->GetTopLevelContentDocument();
95
0
    nsIURI *topLevelDocURI = topLevelDocument ? topLevelDocument->GetDocumentURI() : nullptr;
96
0
    nsCString topLevelDocURISpec;
97
0
    if (topLevelDocURI) {
98
0
        topLevelDocURI->GetSpec(topLevelDocURISpec);
99
0
    }
100
0
101
0
    // Load Third Party Util service.
102
0
    nsresult rv;
103
0
    nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
104
0
        do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
105
0
    NS_ENSURE_SUCCESS(rv, false);
106
0
107
0
    // Block all third-party attempts to extract canvas.
108
0
    bool isThirdParty = true;
109
0
    rv = thirdPartyUtil->IsThirdPartyURI(topLevelDocURI, docURI, &isThirdParty);
110
0
    NS_ENSURE_SUCCESS(rv, false);
111
0
    if (isThirdParty) {
112
0
        nsAutoCString message;
113
0
        message.AppendPrintf("Blocked third party %s in page %s from extracting canvas data.",
114
0
                             docURISpec.get(), topLevelDocURISpec.get());
115
0
        if (isScriptKnown) {
116
0
            message.AppendPrintf(" %s:%u.", scriptFile.get(), scriptLine);
117
0
        }
118
0
        nsContentUtils::LogMessageToConsole(message.get());
119
0
        return false;
120
0
    }
121
0
122
0
    // Load Permission Manager service.
123
0
    nsCOMPtr<nsIPermissionManager> permissionManager =
124
0
        do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
125
0
    NS_ENSURE_SUCCESS(rv, false);
126
0
127
0
    // Check if the site has permission to extract canvas data.
128
0
    // Either permit or block extraction if a stored permission setting exists.
129
0
    uint32_t permission;
130
0
    rv = permissionManager->TestPermission(topLevelDocURI,
131
0
                                           PERMISSION_CANVAS_EXTRACT_DATA,
132
0
                                           &permission);
133
0
    NS_ENSURE_SUCCESS(rv, false);
134
0
    switch (permission) {
135
0
    case nsIPermissionManager::ALLOW_ACTION:
136
0
        return true;
137
0
    case nsIPermissionManager::DENY_ACTION:
138
0
        return false;
139
0
    default:
140
0
        break;
141
0
    }
142
0
143
0
    // At this point, permission is unknown (nsIPermissionManager::UNKNOWN_ACTION).
144
0
145
0
    // Check if the request is in response to user input
146
0
    if (StaticPrefs::privacy_resistFingerprinting_autoDeclineNoUserInputCanvasPrompts() &&
147
0
        !EventStateManager::IsHandlingUserInput()) {
148
0
        nsAutoCString message;
149
0
        message.AppendPrintf("Blocked %s in page %s from extracting canvas data because no user input was detected.",
150
0
                             docURISpec.get(), topLevelDocURISpec.get());
151
0
        if (isScriptKnown) {
152
0
            message.AppendPrintf(" %s:%u.", scriptFile.get(), scriptLine);
153
0
        }
154
0
        nsContentUtils::LogMessageToConsole(message.get());
155
0
156
0
        return false;
157
0
    }
158
0
159
0
    // It was in response to user input, so log and display the prompt.
160
0
    nsAutoCString message;
161
0
    message.AppendPrintf("Blocked %s in page %s from extracting canvas data, but prompting the user.",
162
0
                         docURISpec.get(), topLevelDocURISpec.get());
163
0
    if (isScriptKnown) {
164
0
        message.AppendPrintf(" %s:%u.", scriptFile.get(), scriptLine);
165
0
    }
166
0
    nsContentUtils::LogMessageToConsole(message.get());
167
0
168
0
    // Prompt the user (asynchronous).
169
0
    nsPIDOMWindowOuter *win = aDocument->GetWindow();
170
0
    if (XRE_IsContentProcess()) {
171
0
        TabChild* tabChild = TabChild::GetFrom(win);
172
0
        if (tabChild) {
173
0
            tabChild->SendShowCanvasPermissionPrompt(topLevelDocURISpec);
174
0
        }
175
0
    } else {
176
0
        nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
177
0
        if (obs) {
178
0
            obs->NotifyObservers(win, TOPIC_CANVAS_PERMISSIONS_PROMPT,
179
0
                                 NS_ConvertUTF8toUTF16(topLevelDocURISpec).get());
180
0
        }
181
0
    }
182
0
183
0
    // We don't extract the image for now -- user may override at prompt.
184
0
    return false;
185
0
}
186
187
bool
188
GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_type)
189
0
{
190
0
  if (str.EqualsLiteral("2d")) {
191
0
    *out_type = dom::CanvasContextType::Canvas2D;
192
0
    return true;
193
0
  }
194
0
195
0
  if (str.EqualsLiteral("webgl") ||
196
0
      str.EqualsLiteral("experimental-webgl"))
197
0
  {
198
0
    *out_type = dom::CanvasContextType::WebGL1;
199
0
    return true;
200
0
  }
201
0
202
0
  if (WebGL2Context::IsSupported()) {
203
0
    if (str.EqualsLiteral("webgl2")) {
204
0
      *out_type = dom::CanvasContextType::WebGL2;
205
0
      return true;
206
0
    }
207
0
  }
208
0
209
0
  if (str.EqualsLiteral("bitmaprenderer")) {
210
0
    *out_type = dom::CanvasContextType::ImageBitmap;
211
0
    return true;
212
0
  }
213
0
214
0
  return false;
215
0
}
216
217
/**
218
 * This security check utility might be called from an source that never taints
219
 * others. For example, while painting a CanvasPattern, which is created from an
220
 * ImageBitmap, onto a canvas. In this case, the caller could set the CORSUsed
221
 * true in order to pass this check and leave the aPrincipal to be a nullptr
222
 * since the aPrincipal is not going to be used.
223
 */
224
void
225
DoDrawImageSecurityCheck(dom::HTMLCanvasElement *aCanvasElement,
226
                         nsIPrincipal *aPrincipal,
227
                         bool forceWriteOnly,
228
                         bool CORSUsed)
229
0
{
230
0
    // Callers should ensure that mCanvasElement is non-null before calling this
231
0
    if (!aCanvasElement) {
232
0
        NS_WARNING("DoDrawImageSecurityCheck called without canvas element!");
233
0
        return;
234
0
    }
235
0
236
0
    if (aCanvasElement->IsWriteOnly())
237
0
        return;
238
0
239
0
    // If we explicitly set WriteOnly just do it and get out
240
0
    if (forceWriteOnly) {
241
0
        aCanvasElement->SetWriteOnly();
242
0
        return;
243
0
    }
244
0
245
0
    // No need to do a security check if the image used CORS for the load
246
0
    if (CORSUsed)
247
0
        return;
248
0
249
0
    MOZ_ASSERT(aPrincipal, "Must have a principal here");
250
0
251
0
    if (aCanvasElement->NodePrincipal()->Subsumes(aPrincipal)) {
252
0
        // This canvas has access to that image anyway
253
0
        return;
254
0
    }
255
0
256
0
    aCanvasElement->SetWriteOnly();
257
0
}
258
259
bool
260
CoerceDouble(const JS::Value& v, double* d)
261
0
{
262
0
    if (v.isDouble()) {
263
0
        *d = v.toDouble();
264
0
    } else if (v.isInt32()) {
265
0
        *d = double(v.toInt32());
266
0
    } else if (v.isUndefined()) {
267
0
        *d = 0.0;
268
0
    } else {
269
0
        return false;
270
0
    }
271
0
    return true;
272
0
}
273
274
bool
275
HasDrawWindowPrivilege(JSContext* aCx, JSObject* /* unused */)
276
0
{
277
0
  return nsContentUtils::CallerHasPermission(aCx, nsGkAtoms::all_urlsPermission);
278
0
}
279
280
} // namespace CanvasUtils
281
} // namespace mozilla