forked from mirrors/gecko-dev
Bug 1431255 - Part III, Create per-origin sandboxes from XPCJSRuntime and load UA widgets scripts r=bholley,jaws,sfink
This patch creates the basic structure on how the widget scripts can be loaded and be pointed to the Shadow Root, from the UAWidgetsChild.jsm. The UAWidgetsClass class asks for a sandbox from Cu.getUAWidgetScope(), which calls into XPCJSRuntime::GetUAWidgetScope(). It creates and keeps the sandboxes, in a GCHashMap keyed to the origin, so we could reuse it if needed. MozReview-Commit-ID: J6W4PDQWMcN --HG-- extra : rebase_source : a62b0a22195f09cdb508df72c954e20d18c7bf68
This commit is contained in:
parent
8cc930296b
commit
bfd7aeb85d
8 changed files with 192 additions and 0 deletions
87
browser/actors/UAWidgetsChild.jsm
Normal file
87
browser/actors/UAWidgetsChild.jsm
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
/* vim: set ts=2 sw=2 sts=2 et tw=80: */
|
||||||
|
/* 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/. */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var EXPORTED_SYMBOLS = ["UAWidgetsChild"];
|
||||||
|
|
||||||
|
ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
|
||||||
|
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
class UAWidgetsChild extends ActorChild {
|
||||||
|
constructor(mm) {
|
||||||
|
super(mm);
|
||||||
|
|
||||||
|
this.widgets = new WeakMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(aEvent) {
|
||||||
|
switch (aEvent.type) {
|
||||||
|
case "UAWidgetBindToTree":
|
||||||
|
case "UAWidgetAttributeChanged":
|
||||||
|
this.setupOrNotifyWidget(aEvent.target);
|
||||||
|
break;
|
||||||
|
case "UAWidgetUnbindFromTree":
|
||||||
|
this.teardownWidget(aEvent.target);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupOrNotifyWidget(aElement) {
|
||||||
|
let widget = this.widgets.get(aElement);
|
||||||
|
if (!widget) {
|
||||||
|
this.setupWidget(aElement);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof widget.wrappedJSObject.onattributechange == "function") {
|
||||||
|
widget.wrappedJSObject.onattributechange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupWidget(aElement) {
|
||||||
|
let uri;
|
||||||
|
let widgetName;
|
||||||
|
switch (aElement.localName) {
|
||||||
|
case "video":
|
||||||
|
case "audio":
|
||||||
|
uri = "chrome://global/content/elements/videocontrols.js";
|
||||||
|
widgetName = "VideoControlsPageWidget";
|
||||||
|
break;
|
||||||
|
case "input":
|
||||||
|
// TODO (datetimebox)
|
||||||
|
break;
|
||||||
|
case "applet":
|
||||||
|
case "embed":
|
||||||
|
case "object":
|
||||||
|
// TODO (pluginProblems)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!uri || !widgetName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let shadowRoot = aElement.openOrClosedShadowRoot;
|
||||||
|
let sandbox = aElement.nodePrincipal.isSystemPrincipal ?
|
||||||
|
Object.create(null) : Cu.getUAWidgetScope(aElement.nodePrincipal);
|
||||||
|
|
||||||
|
if (!sandbox[widgetName]) {
|
||||||
|
Services.scriptloader.loadSubScript(uri, sandbox, "UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
let widget = new sandbox[widgetName](shadowRoot);
|
||||||
|
this.widgets.set(aElement, widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
teardownWidget(aElement) {
|
||||||
|
let widget = this.widgets.get(aElement);
|
||||||
|
if (!widget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof widget.wrappedJSObject.destructor == "function") {
|
||||||
|
widget.wrappedJSObject.destructor();
|
||||||
|
}
|
||||||
|
this.widgets.delete(aElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,6 +37,7 @@ FINAL_TARGET_FILES.actors += [
|
||||||
'PageMetadataChild.jsm',
|
'PageMetadataChild.jsm',
|
||||||
'PageStyleChild.jsm',
|
'PageStyleChild.jsm',
|
||||||
'PluginChild.jsm',
|
'PluginChild.jsm',
|
||||||
|
'UAWidgetsChild.jsm',
|
||||||
'URIFixupChild.jsm',
|
'URIFixupChild.jsm',
|
||||||
'WebRTCChild.jsm',
|
'WebRTCChild.jsm',
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,18 @@ let ACTORS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
UAWidgets: {
|
||||||
|
child: {
|
||||||
|
module: "resource:///actors/UAWidgetsChild.jsm",
|
||||||
|
group: "browsers",
|
||||||
|
events: {
|
||||||
|
"UAWidgetBindToTree": {},
|
||||||
|
"UAWidgetAttributeChanged": {},
|
||||||
|
"UAWidgetUnbindFromTree": {}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
UITour: {
|
UITour: {
|
||||||
child: {
|
child: {
|
||||||
module: "resource:///modules/UITourChild.jsm",
|
module: "resource:///modules/UITourChild.jsm",
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,8 @@ public:
|
||||||
!BasePrincipal::Cast(aDocumentPrincipal)->AddonPolicy());
|
!BasePrincipal::Cast(aDocumentPrincipal)->AddonPolicy());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t GetOriginNoSuffixHash() const { return mOriginNoSuffix->hash(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~BasePrincipal();
|
virtual ~BasePrincipal();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ interface nsIJSCID;
|
||||||
interface nsIJSIID;
|
interface nsIJSIID;
|
||||||
interface nsIPrincipal;
|
interface nsIPrincipal;
|
||||||
interface nsIStackFrame;
|
interface nsIStackFrame;
|
||||||
|
webidl Element;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* interface of Components.interfacesByID
|
* interface of Components.interfacesByID
|
||||||
|
|
@ -175,6 +176,13 @@ interface nsIXPCComponents_Utils : nsISupports
|
||||||
[optional] in AUTF8String filename,
|
[optional] in AUTF8String filename,
|
||||||
[optional] in long lineNo);
|
[optional] in long lineNo);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the sandbox for running JS-implemented UA widgets (video controls etc.),
|
||||||
|
* hosted inside UA-created Shadow DOM.
|
||||||
|
*/
|
||||||
|
[implicit_jscontext]
|
||||||
|
jsval getUAWidgetScope(in nsIPrincipal principal);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* getSandboxMetadata is designed to be called from JavaScript only.
|
* getSandboxMetadata is designed to be called from JavaScript only.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -2176,6 +2176,21 @@ nsXPCComponents_Utils::EvalInSandbox(const nsAString& source,
|
||||||
return xpc::EvalInSandbox(cx, sandbox, source, filename, lineNo, retval);
|
return xpc::EvalInSandbox(cx, sandbox, source, filename, lineNo, retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsXPCComponents_Utils::GetUAWidgetScope(nsIPrincipal* principal,
|
||||||
|
JSContext* cx,
|
||||||
|
MutableHandleValue rval)
|
||||||
|
{
|
||||||
|
rval.set(UndefinedValue());
|
||||||
|
|
||||||
|
JSObject* scope = XPCJSRuntime::Get()->GetUAWidgetScope(cx, principal);
|
||||||
|
NS_ENSURE_TRUE(scope, NS_ERROR_OUT_OF_MEMORY); // See bug 858642.
|
||||||
|
|
||||||
|
rval.set(JS::ObjectValue(*scope));
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsXPCComponents_Utils::GetSandboxMetadata(HandleValue sandboxVal,
|
nsXPCComponents_Utils::GetSandboxMetadata(HandleValue sandboxVal,
|
||||||
JSContext* cx, MutableHandleValue rval)
|
JSContext* cx, MutableHandleValue rval)
|
||||||
|
|
|
||||||
|
|
@ -898,6 +898,7 @@ XPCJSRuntime::WeakPointerZonesCallback(JSContext* cx, void* data)
|
||||||
XPCJSRuntime* self = static_cast<XPCJSRuntime*>(data);
|
XPCJSRuntime* self = static_cast<XPCJSRuntime*>(data);
|
||||||
|
|
||||||
self->mWrappedJSMap->UpdateWeakPointersAfterGC();
|
self->mWrappedJSMap->UpdateWeakPointersAfterGC();
|
||||||
|
self->mUAWidgetScopeMap.sweep();
|
||||||
|
|
||||||
XPCWrappedNativeScope::UpdateWeakPointersInAllScopesAfterGC();
|
XPCWrappedNativeScope::UpdateWeakPointersInAllScopesAfterGC();
|
||||||
}
|
}
|
||||||
|
|
@ -2827,6 +2828,7 @@ XPCJSRuntime::XPCJSRuntime(JSContext* aCx)
|
||||||
mWrappedJSRoots(nullptr),
|
mWrappedJSRoots(nullptr),
|
||||||
mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite())
|
mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite())
|
||||||
{
|
{
|
||||||
|
MOZ_ALWAYS_TRUE(mUAWidgetScopeMap.init());
|
||||||
MOZ_COUNT_CTOR_INHERITED(XPCJSRuntime, CycleCollectedJSRuntime);
|
MOZ_COUNT_CTOR_INHERITED(XPCJSRuntime, CycleCollectedJSRuntime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3139,6 +3141,43 @@ XPCJSRuntime::RemoveGCCallback(xpcGCCallback cb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSObject*
|
||||||
|
XPCJSRuntime::GetUAWidgetScope(JSContext* cx, nsIPrincipal* principal)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(principal),
|
||||||
|
"Running UA Widget in chrome");
|
||||||
|
|
||||||
|
RefPtr<BasePrincipal> key = BasePrincipal::Cast(principal);
|
||||||
|
if (Principal2JSObjectMap::Ptr p = mUAWidgetScopeMap.lookup(key)) {
|
||||||
|
return p->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
SandboxOptions options;
|
||||||
|
options.sandboxName.AssignLiteral("UA Widget Scope");
|
||||||
|
options.wantXrays = false;
|
||||||
|
options.wantComponents = false;
|
||||||
|
|
||||||
|
// Use an ExpandedPrincipal to create asymmetric security.
|
||||||
|
MOZ_ASSERT(!nsContentUtils::IsExpandedPrincipal(principal));
|
||||||
|
nsTArray<nsCOMPtr<nsIPrincipal>> principalAsArray(1);
|
||||||
|
principalAsArray.AppendElement(principal);
|
||||||
|
RefPtr<ExpandedPrincipal> ep =
|
||||||
|
ExpandedPrincipal::Create(principalAsArray,
|
||||||
|
principal->OriginAttributesRef());
|
||||||
|
|
||||||
|
// Create the sandbox.
|
||||||
|
RootedValue v(cx);
|
||||||
|
nsresult rv = CreateSandboxObject(cx, &v,
|
||||||
|
static_cast<nsIExpandedPrincipal*>(ep),
|
||||||
|
options);
|
||||||
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||||
|
JSObject* scope = &v.toObject();
|
||||||
|
|
||||||
|
MOZ_ALWAYS_TRUE(mUAWidgetScopeMap.putNew(key, scope));
|
||||||
|
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
XPCJSRuntime::InitSingletonScopes()
|
XPCJSRuntime::InitSingletonScopes()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "xpcpublic.h"
|
#include "xpcpublic.h"
|
||||||
|
#include "js/HashTable.h"
|
||||||
|
#include "js/GCHashTable.h"
|
||||||
#include "js/TracingAPI.h"
|
#include "js/TracingAPI.h"
|
||||||
#include "js/WeakMapPtr.h"
|
#include "js/WeakMapPtr.h"
|
||||||
#include "PLDHashTable.h"
|
#include "PLDHashTable.h"
|
||||||
|
|
@ -572,6 +574,8 @@ public:
|
||||||
void AddGCCallback(xpcGCCallback cb);
|
void AddGCCallback(xpcGCCallback cb);
|
||||||
void RemoveGCCallback(xpcGCCallback cb);
|
void RemoveGCCallback(xpcGCCallback cb);
|
||||||
|
|
||||||
|
JSObject* GetUAWidgetScope(JSContext* cx, nsIPrincipal* principal);
|
||||||
|
|
||||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||||
|
|
||||||
JSObject* UnprivilegedJunkScope() { return mUnprivilegedJunkScope; }
|
JSObject* UnprivilegedJunkScope() { return mUnprivilegedJunkScope; }
|
||||||
|
|
@ -595,11 +599,35 @@ private:
|
||||||
jsid mStrIDs[XPCJSContext::IDX_TOTAL_COUNT];
|
jsid mStrIDs[XPCJSContext::IDX_TOTAL_COUNT];
|
||||||
JS::Value mStrJSVals[XPCJSContext::IDX_TOTAL_COUNT];
|
JS::Value mStrJSVals[XPCJSContext::IDX_TOTAL_COUNT];
|
||||||
|
|
||||||
|
struct Hasher {
|
||||||
|
typedef RefPtr<mozilla::BasePrincipal> Key;
|
||||||
|
typedef Key Lookup;
|
||||||
|
static uint32_t hash(const Lookup& l) {
|
||||||
|
return l->GetOriginNoSuffixHash();
|
||||||
|
}
|
||||||
|
static bool match(const Key& k, const Lookup& l) {
|
||||||
|
return k->FastEquals(l);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SweepPolicy {
|
||||||
|
static bool needsSweep(RefPtr<mozilla::BasePrincipal>* /* unused */, JS::Heap<JSObject*>* value) {
|
||||||
|
return JS::GCPolicy<JS::Heap<JSObject*>>::needsSweep(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef JS::GCHashMap<RefPtr<mozilla::BasePrincipal>,
|
||||||
|
JS::Heap<JSObject*>,
|
||||||
|
Hasher,
|
||||||
|
js::SystemAllocPolicy,
|
||||||
|
SweepPolicy> Principal2JSObjectMap;
|
||||||
|
|
||||||
JSObject2WrappedJSMap* mWrappedJSMap;
|
JSObject2WrappedJSMap* mWrappedJSMap;
|
||||||
IID2WrappedJSClassMap* mWrappedJSClassMap;
|
IID2WrappedJSClassMap* mWrappedJSClassMap;
|
||||||
IID2NativeInterfaceMap* mIID2NativeInterfaceMap;
|
IID2NativeInterfaceMap* mIID2NativeInterfaceMap;
|
||||||
ClassInfo2NativeSetMap* mClassInfo2NativeSetMap;
|
ClassInfo2NativeSetMap* mClassInfo2NativeSetMap;
|
||||||
NativeSetMap* mNativeSetMap;
|
NativeSetMap* mNativeSetMap;
|
||||||
|
Principal2JSObjectMap mUAWidgetScopeMap;
|
||||||
XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap;
|
XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap;
|
||||||
bool mGCIsRunning;
|
bool mGCIsRunning;
|
||||||
nsTArray<nsISupports*> mNativesToReleaseArray;
|
nsTArray<nsISupports*> mNativesToReleaseArray;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue