summaryrefslogtreecommitdiffstats
path: root/dom/canvas/CanvasUtils.h
blob: 801f5e01b9306f3b032782e931cca378899a980e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef _CANVASUTILS_H_
#define _CANVASUTILS_H_

#include "CanvasRenderingContextHelper.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/dom/ToJSValue.h"
#include "jsapi.h"
#include "js/Array.h"               // JS::GetArrayLength
#include "js/PropertyAndElement.h"  // JS_GetElement
#include "mozilla/FloatingPoint.h"

class nsIPrincipal;

namespace mozilla {

namespace dom {
class Document;
class HTMLCanvasElement;
class OffscreenCanvas;
}  // namespace dom

namespace CanvasUtils {

bool GetCanvasContextType(const nsAString& str,
                          dom::CanvasContextType* const out_type);

// Check that the rectangle [x,y,w,h] is a subrectangle of
// [0,0,realWidth,realHeight]

inline bool CheckSaneSubrectSize(int32_t x, int32_t y, int32_t w, int32_t h,
                                 int32_t realWidth, int32_t realHeight) {
  CheckedInt32 checked_xmost = CheckedInt32(x) + w;
  CheckedInt32 checked_ymost = CheckedInt32(y) + h;

  return w >= 0 && h >= 0 && x >= 0 && y >= 0 && checked_xmost.isValid() &&
         checked_xmost.value() <= realWidth && checked_ymost.isValid() &&
         checked_ymost.value() <= realHeight;
}

// Flag aCanvasElement as write-only if drawing an image with aPrincipal
// onto it would make it such.

void DoDrawImageSecurityCheck(dom::HTMLCanvasElement* aCanvasElement,
                              nsIPrincipal* aPrincipal, bool forceWriteOnly,
                              bool CORSUsed);

void DoDrawImageSecurityCheck(dom::OffscreenCanvas* aOffscreenCanvas,
                              nsIPrincipal* aPrincipal, bool forceWriteOnly,
                              bool CORSUsed);

// Check if the context is chrome or has the permission to drawWindow
bool HasDrawWindowPrivilege(JSContext* aCx, JSObject* aObj);

// Check if the context has permission to use OffscreenCanvas.
bool IsOffscreenCanvasEnabled(JSContext* aCx, JSObject* aObj);

// Check site-specific permission and display prompt if appropriate.
bool IsImageExtractionAllowed(dom::Document* aDocument, JSContext* aCx,
                              nsIPrincipal& aPrincipal);

enum class ImageExtraction {
  Unrestricted,
  Placeholder,
  Randomize,
};

// Returns whether the result of an image extraction should be replaced
// by a placeholder or randomized.
ImageExtraction ImageExtractionResult(dom::HTMLCanvasElement* aCanvasElement,
                                      JSContext* aCx, nsIPrincipal& aPrincipal);
ImageExtraction ImageExtractionResult(dom::OffscreenCanvas* aOffscreenCanvas,
                                      JSContext* aCx, nsIPrincipal& aPrincipal);

// Make a double out of |v|, treating undefined values as 0.0 (for
// the sake of sparse arrays).  Return true iff coercion
// succeeded.
bool CoerceDouble(const JS::Value& v, double* d);

/* Float validation stuff */
#define VALIDATE(_f) \
  if (!std::isfinite(_f)) return false

inline bool FloatValidate(double f1) {
  VALIDATE(f1);
  return true;
}

inline bool FloatValidate(double f1, double f2) {
  VALIDATE(f1);
  VALIDATE(f2);
  return true;
}

inline bool FloatValidate(double f1, double f2, double f3) {
  VALIDATE(f1);
  VALIDATE(f2);
  VALIDATE(f3);
  return true;
}

inline bool FloatValidate(double f1, double f2, double f3, double f4) {
  VALIDATE(f1);
  VALIDATE(f2);
  VALIDATE(f3);
  VALIDATE(f4);
  return true;
}

inline bool FloatValidate(double f1, double f2, double f3, double f4,
                          double f5) {
  VALIDATE(f1);
  VALIDATE(f2);
  VALIDATE(f3);
  VALIDATE(f4);
  VALIDATE(f5);
  return true;
}

inline bool FloatValidate(double f1, double f2, double f3, double f4, double f5,
                          double f6) {
  VALIDATE(f1);
  VALIDATE(f2);
  VALIDATE(f3);
  VALIDATE(f4);
  VALIDATE(f5);
  VALIDATE(f6);
  return true;
}

#undef VALIDATE

template <typename T>
nsresult JSValToDashArray(JSContext* cx, const JS::Value& patternArray,
                          nsTArray<T>& dashes) {
  // The cap is pretty arbitrary.  16k should be enough for
  // anybody...
  static const uint32_t MAX_NUM_DASHES = 1 << 14;

  if (!patternArray.isPrimitive()) {
    JS::Rooted<JSObject*> obj(cx, patternArray.toObjectOrNull());
    uint32_t length;
    if (!JS::GetArrayLength(cx, obj, &length)) {
      // Not an array-like thing
      return NS_ERROR_INVALID_ARG;
    } else if (length > MAX_NUM_DASHES) {
      // Too many dashes in the pattern
      return NS_ERROR_ILLEGAL_VALUE;
    }

    bool haveNonzeroElement = false;
    for (uint32_t i = 0; i < length; ++i) {
      JS::Rooted<JS::Value> elt(cx);
      double d;
      if (!JS_GetElement(cx, obj, i, &elt)) {
        return NS_ERROR_FAILURE;
      }
      if (!(CoerceDouble(elt, &d) && FloatValidate(d) && d >= 0.0)) {
        // Pattern elements must be finite "numbers" >= 0.
        return NS_ERROR_INVALID_ARG;
      } else if (d > 0.0) {
        haveNonzeroElement = true;
      }
      if (!dashes.AppendElement(d, mozilla::fallible)) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
    }

    if (dashes.Length() > 0 && !haveNonzeroElement) {
      // An all-zero pattern makes no sense.
      return NS_ERROR_ILLEGAL_VALUE;
    }
  } else if (!(patternArray.isUndefined() || patternArray.isNull())) {
    // undefined and null mean "reset to no dash".  Any other
    // random garbage is a type error.
    return NS_ERROR_INVALID_ARG;
  }

  return NS_OK;
}

template <typename T>
void DashArrayToJSVal(nsTArray<T>& dashes, JSContext* cx,
                      JS::MutableHandle<JS::Value> retval,
                      mozilla::ErrorResult& rv) {
  if (dashes.IsEmpty()) {
    retval.setNull();
    return;
  }
  JS::Rooted<JS::Value> val(cx);
  if (!mozilla::dom::ToJSValue(cx, dashes, retval)) {
    rv.Throw(NS_ERROR_OUT_OF_MEMORY);
  }
}

// returns true if write-only mode must used for this principal based on
// the incumbent global.
bool CheckWriteOnlySecurity(bool aCORSUsed, nsIPrincipal* aPrincipal,
                            bool aHadCrossOriginRedirects);

}  // namespace CanvasUtils
}  // namespace mozilla

#endif /* _CANVASUTILS_H_ */