Bug 1310318 - Part 1: Allow access to canvas drawWindow() with web extensions permission
MozReview-Commit-ID: 4ee3pRfduIj
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -2070,16 +2070,32 @@ bool
nsContentUtils::CanCallerAccess(nsPIDOMWindowInner* aWindow)
{
nsCOMPtr<nsIScriptObjectPrincipal> scriptObject = do_QueryInterface(aWindow);
NS_ENSURE_TRUE(scriptObject, false);
return CanCallerAccess(SubjectPrincipal(), scriptObject->GetPrincipal());
}
+// static
+bool
+nsContentUtils::CallerHasPermission(JSContext* aCx, const nsAString& aPerm)
+{
+ // Chrome gets access by default.
+ if (IsSystemCaller(aCx)) {
+ return true;
+ }
+
+ JSCompartment* c = js::GetContextCompartment(aCx);
+ nsIPrincipal* p = nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
+
+ // Otherwise, only allow if caller is an addon with the permission.
+ return BasePrincipal::Cast(p)->AddonHasPermission(aPerm);
+}
+
//static
bool
nsContentUtils::InProlog(nsINode *aNode)
{
NS_PRECONDITION(aNode, "missing node to nsContentUtils::InProlog");
nsINode* parent = aNode->GetParentNode();
if (!parent || !parent->IsNodeOfType(nsINode::eDOCUMENT)) {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -498,16 +498,19 @@ public:
// Check if the (JS) caller can access aNode.
static bool CanCallerAccess(nsIDOMNode *aNode);
static bool CanCallerAccess(nsINode* aNode);
// Check if the (JS) caller can access aWindow.
// aWindow can be either outer or inner window.
static bool CanCallerAccess(nsPIDOMWindowInner* aWindow);
+ // Check if the JS caller is chrome or an addon with the permission.
+ static bool CallerHasPermission(JSContext* aCx, const nsAString& aPerm);
+
/**
* GetDocumentFromCaller gets its document by looking at the last called
* function and finding the document that the function itself relates to.
* For example, consider two windows A and B in the same origin. B has a
* function which does something that ends up needing the current document.
* If a script in window A were to call B's function, GetDocumentFromCaller
* would find that function (in B) and return B's document.
*
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -5209,29 +5209,16 @@ CanvasRenderingContext2D::DrawWindow(nsG
}
CompositionOp op = UsedOperation();
bool discardContent = GlobalAlpha() == 1.0f
&& (op == CompositionOp::OP_OVER || op == CompositionOp::OP_SOURCE);
const gfx::Rect drawRect(aX, aY, aW, aH);
EnsureTarget(discardContent ? &drawRect : nullptr);
- // We can't allow web apps to call this until we fix at least the
- // following potential security issues:
- // -- rendering cross-domain IFRAMEs and then extracting the results
- // -- rendering the user's theme and then extracting the results
- // -- rendering native anonymous content (e.g., file input paths;
- // scrollbars should be allowed)
- if (!nsContentUtils::IsCallerChrome()) {
- // not permitted to use DrawWindow
- // XXX ERRMSG we need to report an error to developers here! (bug 329026)
- aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
- return;
- }
-
// Flush layout updates
if (!(aFlags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH)) {
nsContentUtils::FlushLayoutForTree(aWindow.AsInner()->GetOuterWindow());
}
RefPtr<nsPresContext> presContext;
nsIDocShell* docshell = aWindow.GetDocShell();
if (docshell) {
--- a/dom/canvas/CanvasUtils.cpp
+++ b/dom/canvas/CanvasUtils.cpp
@@ -121,10 +121,16 @@ CoerceDouble(const JS::Value& v, double*
} else if (v.isUndefined()) {
*d = 0.0;
} else {
return false;
}
return true;
}
+bool
+HasDrawWindowPrivilege(JSContext* aCx, JSObject* /* unused */)
+{
+ return nsContentUtils::CallerHasPermission(aCx, NS_LITERAL_STRING("<all_urls>"));
+}
+
} // namespace CanvasUtils
} // namespace mozilla
--- a/dom/canvas/CanvasUtils.h
+++ b/dom/canvas/CanvasUtils.h
@@ -41,16 +41,19 @@ inline bool CheckSaneSubrectSize(int32_t
// 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);
+// Check if the context is chrome or has the permission to drawWindow
+bool HasDrawWindowPrivilege(JSContext* aCx, JSObject* aObj);
+
// 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 (!IsFinite(_f)) return false
--- a/dom/webidl/CanvasRenderingContext2D.webidl
+++ b/dom/webidl/CanvasRenderingContext2D.webidl
@@ -48,33 +48,33 @@ interface CanvasRenderingContext2D {
attribute DOMString mozTextStyle;
// image smoothing mode -- if disabled, images won't be smoothed
// if scaled.
[Deprecated="PrefixedImageSmoothingEnabled"]
attribute boolean mozImageSmoothingEnabled;
// Show the caret if appropriate when drawing
- [ChromeOnly]
+ [Func="CanvasUtils::HasDrawWindowPrivilege"]
const unsigned long DRAWWINDOW_DRAW_CARET = 0x01;
// Don't flush pending layout notifications that could otherwise
// be batched up
- [ChromeOnly]
+ [Func="CanvasUtils::HasDrawWindowPrivilege"]
const unsigned long DRAWWINDOW_DO_NOT_FLUSH = 0x02;
// Draw scrollbars and scroll the viewport if they are present
- [ChromeOnly]
+ [Func="CanvasUtils::HasDrawWindowPrivilege"]
const unsigned long DRAWWINDOW_DRAW_VIEW = 0x04;
// Use the widget layer manager if available. This means hardware
// acceleration may be used, but it might actually be slower or
// lower quality than normal. It will however more accurately reflect
// the pixels rendered to the screen.
- [ChromeOnly]
+ [Func="CanvasUtils::HasDrawWindowPrivilege"]
const unsigned long DRAWWINDOW_USE_WIDGET_LAYERS = 0x08;
// Don't synchronously decode images - draw what we have
- [ChromeOnly]
+ [Func="CanvasUtils::HasDrawWindowPrivilege"]
const unsigned long DRAWWINDOW_ASYNC_DECODE_IMAGES = 0x10;
/**
* Renders a region of a window into the canvas. The contents of
* the window's viewport are rendered, ignoring viewport clipping
* and scrolling.
*
* @param x
@@ -100,19 +100,19 @@ interface CanvasRenderingContext2D {
* -- Top-level browsed documents are usually not transparent
* because the user's background-color preference is applied,
* but IFRAMEs are transparent if the page doesn't set a background.
* -- If an opaque color is used for the background color, rendering
* will be faster because we won't have to compute the window's
* transparency.
*
* This API cannot currently be used by Web content. It is chrome
- * only.
+ * and Web Extensions (with a permission) only.
*/
- [Throws, ChromeOnly]
+ [Throws, Func="CanvasUtils::HasDrawWindowPrivilege"]
void drawWindow(Window window, double x, double y, double w, double h,
DOMString bgColor, optional unsigned long flags = 0);
[Throws, ChromeOnly]
void asyncDrawXULElement(XULElement elem, double x, double y, double w,
double h, DOMString bgColor,
optional unsigned long flags = 0);
/**
@@ -319,24 +319,24 @@ interface CanvasPathMethods {
void lineTo(double x, double y);
[LenientFloat]
void quadraticCurveTo(double cpx, double cpy, double x, double y);
[LenientFloat]
void bezierCurveTo(double cp1x, double cp1y, double cp2x, double cp2y, double x, double y);
[Throws, LenientFloat]
- void arcTo(double x1, double y1, double x2, double y2, double radius);
+ void arcTo(double x1, double y1, double x2, double y2, double radius);
// NOT IMPLEMENTED [LenientFloat] void arcTo(double x1, double y1, double x2, double y2, double radiusX, double radiusY, double rotation);
[LenientFloat]
void rect(double x, double y, double w, double h);
[Throws, LenientFloat]
- void arc(double x, double y, double radius, double startAngle, double endAngle, optional boolean anticlockwise = false);
+ void arc(double x, double y, double radius, double startAngle, double endAngle, optional boolean anticlockwise = false);
[Throws, LenientFloat]
void ellipse(double x, double y, double radiusX, double radiusY, double rotation, double startAngle, double endAngle, optional boolean anticlockwise = false);
};
[NoInterfaceObject]
interface CanvasHitRegions {
// hit regions