mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
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
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',
|
||||
'PageStyleChild.jsm',
|
||||
'PluginChild.jsm',
|
||||
'UAWidgetsChild.jsm',
|
||||
'URIFixupChild.jsm',
|
||||
'WebRTCChild.jsm',
|
||||
]
|
||||
|
@ -230,6 +230,18 @@ let ACTORS = {
|
||||
},
|
||||
},
|
||||
|
||||
UAWidgets: {
|
||||
child: {
|
||||
module: "resource:///actors/UAWidgetsChild.jsm",
|
||||
group: "browsers",
|
||||
events: {
|
||||
"UAWidgetBindToTree": {},
|
||||
"UAWidgetAttributeChanged": {},
|
||||
"UAWidgetUnbindFromTree": {}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
UITour: {
|
||||
child: {
|
||||
module: "resource:///modules/UITourChild.jsm",
|
||||
|
@ -163,6 +163,8 @@ public:
|
||||
!BasePrincipal::Cast(aDocumentPrincipal)->AddonPolicy());
|
||||
}
|
||||
|
||||
uint32_t GetOriginNoSuffixHash() const { return mOriginNoSuffix->hash(); }
|
||||
|
||||
protected:
|
||||
virtual ~BasePrincipal();
|
||||
|
||||
|
@ -19,6 +19,7 @@ interface nsIJSCID;
|
||||
interface nsIJSIID;
|
||||
interface nsIPrincipal;
|
||||
interface nsIStackFrame;
|
||||
webidl Element;
|
||||
|
||||
/**
|
||||
* interface of Components.interfacesByID
|
||||
@ -175,6 +176,13 @@ interface nsIXPCComponents_Utils : nsISupports
|
||||
[optional] in AUTF8String filename,
|
||||
[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.
|
||||
*
|
||||
|
@ -2176,6 +2176,21 @@ nsXPCComponents_Utils::EvalInSandbox(const nsAString& source,
|
||||
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
|
||||
nsXPCComponents_Utils::GetSandboxMetadata(HandleValue sandboxVal,
|
||||
JSContext* cx, MutableHandleValue rval)
|
||||
|
@ -898,6 +898,7 @@ XPCJSRuntime::WeakPointerZonesCallback(JSContext* cx, void* data)
|
||||
XPCJSRuntime* self = static_cast<XPCJSRuntime*>(data);
|
||||
|
||||
self->mWrappedJSMap->UpdateWeakPointersAfterGC();
|
||||
self->mUAWidgetScopeMap.sweep();
|
||||
|
||||
XPCWrappedNativeScope::UpdateWeakPointersInAllScopesAfterGC();
|
||||
}
|
||||
@ -2827,6 +2828,7 @@ XPCJSRuntime::XPCJSRuntime(JSContext* aCx)
|
||||
mWrappedJSRoots(nullptr),
|
||||
mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite())
|
||||
{
|
||||
MOZ_ALWAYS_TRUE(mUAWidgetScopeMap.init());
|
||||
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
|
||||
XPCJSRuntime::InitSingletonScopes()
|
||||
{
|
||||
|
@ -92,6 +92,8 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "xpcpublic.h"
|
||||
#include "js/HashTable.h"
|
||||
#include "js/GCHashTable.h"
|
||||
#include "js/TracingAPI.h"
|
||||
#include "js/WeakMapPtr.h"
|
||||
#include "PLDHashTable.h"
|
||||
@ -572,6 +574,8 @@ public:
|
||||
void AddGCCallback(xpcGCCallback cb);
|
||||
void RemoveGCCallback(xpcGCCallback cb);
|
||||
|
||||
JSObject* GetUAWidgetScope(JSContext* cx, nsIPrincipal* principal);
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
JSObject* UnprivilegedJunkScope() { return mUnprivilegedJunkScope; }
|
||||
@ -595,11 +599,35 @@ private:
|
||||
jsid mStrIDs[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;
|
||||
IID2WrappedJSClassMap* mWrappedJSClassMap;
|
||||
IID2NativeInterfaceMap* mIID2NativeInterfaceMap;
|
||||
ClassInfo2NativeSetMap* mClassInfo2NativeSetMap;
|
||||
NativeSetMap* mNativeSetMap;
|
||||
Principal2JSObjectMap mUAWidgetScopeMap;
|
||||
XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap;
|
||||
bool mGCIsRunning;
|
||||
nsTArray<nsISupports*> mNativesToReleaseArray;
|
||||
|
Loading…
Reference in New Issue
Block a user