forked from mirrors/gecko-dev
Backed out changeset 1a4fe24a016c (bug 1541557)
Backed out changeset 6fc41e51bcee (bug 1561061)
Backed out changeset d916d89a5c90 (bug 1561122)
Backed out changeset 757d285aafdd (bug 1560400)
Backed out changeset a7eab5ca061d (bug 1541557)
Backed out changeset d972bbdfe039 (bug 1541557)
Backed out changeset 8802daac6779 (bug 1541557)
Backed out changeset 92c01418b96f (bug 1561150)
Backed out changeset fa5e186e1635 (bug 1561061)
Backed out changeset aa2bee0b18c3 (bug 1560400)
Backed out changeset adf832af8e48 (bug 1561150)
Backed out changeset 72630a7c6e67 (bug 1561999)
Backed out changeset c35aff2a9336 (bug 1561724)
Backed out changeset 19e0edc92077 (bug 1561150)
Backed out changeset 0b3e2164f128 (bug 1561150)
Backed out changeset 43211ebfe738 (bug 1561122)
Backed out changeset c2d0956f41d8 (bug 1561061)
Backed out changeset bf0f0e95c61c (bug 1560400)
Backed out changeset 84633034590f (bug 1560400)
Backed out changeset d5415970da5f (bug 1532795)
Backed out changeset 119caddcb066 (bug 1532795)
Backed out changeset fbbe113aeef2 (bug 1532795)
Backed out changeset 8a3d311c7fac (bug 1532795)
Backed out changeset 1471732eca80 (bug 1532795)
Backed out changeset 46ff845a7b0c (bug 1541557)
Backed out changeset c2697f04d38c (bug 1541557)
Backed out changeset 75ebd6fce136 (bug 1541557)
Backed out changeset 189dc8a35981 (bug 1541557)
Backed out changeset b4ed40bea269 (bug 1541557)
Backed out changeset 158a4000c44b (bug 1541557)
Backed out changeset 61fa2745733f (bug 1541557)
Backed out changeset d2ee912c5189 (bug 1558298)
Backed out changeset 7a0aab00327b (bug 1558298)
Backed out changeset fddf2808fedf (bug 1558298)
Backed out changeset 0f6b382f0626 (bug 1558298)
Backed out changeset 6ccaa25367f2 (bug 1558298)
Backed out changeset d27574cfbb0e (bug 1558298)
Backed out changeset 162bc1fc2730 (bug 1558298)
Backed out changeset f94500dd11e3 (bug 1558298)
Backed out changeset fb67ac962bc5 (bug 1558298)
Backed out changeset c634099abb9d (bug 1558298)
Backed out changeset 8d4419c439e1 (bug 1558298)
Backed out changeset d8b7ed5e149f (bug 1558298)
--HG--
rename : testing/mochitest/tests/SimpleTest/MozillaLogger.js => testing/specialpowers/content/MozillaLogger.js
rename : testing/specialpowers/content/SpecialPowersParent.jsm => testing/specialpowers/content/SpecialPowersObserver.jsm
rename : testing/specialpowers/content/SpecialPowersAPIParent.jsm => testing/specialpowers/content/SpecialPowersObserverAPI.js
rename : testing/specialpowers/content/SpecialPowersChild.jsm => testing/specialpowers/content/specialpowers.js
rename : testing/specialpowers/content/SpecialPowersAPI.jsm => testing/specialpowers/content/specialpowersAPI.js
266 lines
9.6 KiB
C++
266 lines
9.6 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 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/. */
|
|
|
|
#include "WindowNamedPropertiesHandler.h"
|
|
#include "mozilla/dom/EventTargetBinding.h"
|
|
#include "mozilla/dom/WindowBinding.h"
|
|
#include "mozilla/dom/WindowProxyHolder.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsDOMWindowList.h"
|
|
#include "nsGlobalWindow.h"
|
|
#include "nsHTMLDocument.h"
|
|
#include "nsJSUtils.h"
|
|
#include "xpcprivate.h"
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
static bool ShouldExposeChildWindow(nsString& aNameBeingResolved,
|
|
BrowsingContext* aChild) {
|
|
nsPIDOMWindowOuter* child = aChild->GetDOMWindow();
|
|
Element* e = child->GetFrameElementInternal();
|
|
if (e && e->IsInShadowTree()) {
|
|
return false;
|
|
}
|
|
|
|
// If we're same-origin with the child, go ahead and expose it.
|
|
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(child);
|
|
NS_ENSURE_TRUE(sop, false);
|
|
if (nsContentUtils::SubjectPrincipal()->Equals(sop->GetPrincipal())) {
|
|
return true;
|
|
}
|
|
|
|
// If we're not same-origin, expose it _only_ if the name of the browsing
|
|
// context matches the 'name' attribute of the frame element in the parent.
|
|
// The motivations behind this heuristic are worth explaining here.
|
|
//
|
|
// Historically, all UAs supported global named access to any child browsing
|
|
// context (that is to say, window.dolske returns a child frame where either
|
|
// the "name" attribute on the frame element was set to "dolske", or where
|
|
// the child explicitly set window.name = "dolske").
|
|
//
|
|
// This is problematic because it allows possibly-malicious and unrelated
|
|
// cross-origin subframes to pollute the global namespace of their parent in
|
|
// unpredictable ways (see bug 860494). This is also problematic for browser
|
|
// engines like Servo that want to run cross-origin script on different
|
|
// threads.
|
|
//
|
|
// The naive solution here would be to filter out any cross-origin subframes
|
|
// obtained when doing named lookup in global scope. But that is unlikely to
|
|
// be web-compatible, since it will break named access for consumers that do
|
|
// <iframe name="dolske" src="http://cross-origin.com/sadtrombone.html"> and
|
|
// expect to be able to access the cross-origin subframe via named lookup on
|
|
// the global.
|
|
//
|
|
// The optimal behavior would be to do the following:
|
|
// (a) Look for any child browsing context with name="dolske".
|
|
// (b) If the result is cross-origin, null it out.
|
|
// (c) If we have null, look for a frame element whose 'name' attribute is
|
|
// "dolske".
|
|
//
|
|
// Unfortunately, (c) would require some engineering effort to be performant
|
|
// in Gecko, and probably in other UAs as well. So we go with a simpler
|
|
// approximation of the above. This approximation will only break sites that
|
|
// rely on their cross-origin subframes setting window.name to a known value,
|
|
// which is unlikely to be very common. And while it does introduce a
|
|
// dependency on cross-origin state when doing global lookups, it doesn't
|
|
// allow the child to arbitrarily pollute the parent namespace, and requires
|
|
// cross-origin communication only in a limited set of cases that can be
|
|
// computed independently by the parent.
|
|
return e && e->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
|
|
aNameBeingResolved, eCaseMatters);
|
|
}
|
|
|
|
bool WindowNamedPropertiesHandler::getOwnPropDescriptor(
|
|
JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
|
|
bool /* unused */, JS::MutableHandle<JS::PropertyDescriptor> aDesc) const {
|
|
if (!JSID_IS_STRING(aId)) {
|
|
// Nothing to do if we're resolving a non-string property.
|
|
return true;
|
|
}
|
|
|
|
bool hasOnPrototype;
|
|
if (!HasPropertyOnPrototype(aCx, aProxy, aId, &hasOnPrototype)) {
|
|
return false;
|
|
}
|
|
if (hasOnPrototype) {
|
|
return true;
|
|
}
|
|
|
|
nsAutoJSString str;
|
|
if (!str.init(aCx, JSID_TO_STRING(aId))) {
|
|
return false;
|
|
}
|
|
|
|
if (str.IsEmpty()) {
|
|
return true;
|
|
}
|
|
|
|
// Grab the DOM window.
|
|
nsGlobalWindowInner* win = xpc::WindowGlobalOrNull(aProxy);
|
|
if (win->Length() > 0) {
|
|
RefPtr<BrowsingContext> child = win->GetChildWindow(str);
|
|
if (child && ShouldExposeChildWindow(str, child)) {
|
|
// We found a subframe of the right name. Shadowing via |var foo| in
|
|
// global scope is still allowed, since |var| only looks up |own|
|
|
// properties. But unqualified shadowing will fail, per-spec.
|
|
JS::Rooted<JS::Value> v(aCx);
|
|
if (!ToJSValue(aCx, WindowProxyHolder(child.forget()), &v)) {
|
|
return false;
|
|
}
|
|
FillPropertyDescriptor(aDesc, aProxy, 0, v);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// The rest of this function is for HTML documents only.
|
|
Document* doc = win->GetExtantDoc();
|
|
if (!doc || !doc->IsHTMLOrXHTML()) {
|
|
return true;
|
|
}
|
|
nsHTMLDocument* document = doc->AsHTMLDocument();
|
|
|
|
JS::Rooted<JS::Value> v(aCx);
|
|
Element* element = document->GetElementById(str);
|
|
if (element) {
|
|
if (!ToJSValue(aCx, element, &v)) {
|
|
return false;
|
|
}
|
|
FillPropertyDescriptor(aDesc, aProxy, 0, v);
|
|
return true;
|
|
}
|
|
|
|
ErrorResult rv;
|
|
bool found = document->ResolveName(aCx, str, &v, rv);
|
|
if (rv.MaybeSetPendingException(aCx)) {
|
|
return false;
|
|
}
|
|
|
|
if (found) {
|
|
FillPropertyDescriptor(aDesc, aProxy, 0, v);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool WindowNamedPropertiesHandler::defineProperty(
|
|
JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
|
|
JS::Handle<JS::PropertyDescriptor> aDesc,
|
|
JS::ObjectOpResult& result) const {
|
|
ErrorResult rv;
|
|
rv.ThrowTypeError<MSG_DEFINEPROPERTY_ON_GSP>();
|
|
MOZ_ALWAYS_TRUE(rv.MaybeSetPendingException(aCx));
|
|
return false;
|
|
}
|
|
|
|
bool WindowNamedPropertiesHandler::ownPropNames(
|
|
JSContext* aCx, JS::Handle<JSObject*> aProxy, unsigned flags,
|
|
JS::MutableHandleVector<jsid> aProps) const {
|
|
if (!(flags & JSITER_HIDDEN)) {
|
|
// None of our named properties are enumerable.
|
|
return true;
|
|
}
|
|
|
|
// Grab the DOM window.
|
|
nsGlobalWindowInner* win = xpc::WindowGlobalOrNull(aProxy);
|
|
nsTArray<nsString> names;
|
|
// The names live on the outer window, which might be null
|
|
nsGlobalWindowOuter* outer = win->GetOuterWindowInternal();
|
|
if (outer) {
|
|
nsDOMWindowList* childWindows = outer->GetFrames();
|
|
if (childWindows) {
|
|
uint32_t length = childWindows->GetLength();
|
|
for (uint32_t i = 0; i < length; ++i) {
|
|
nsCOMPtr<nsIDocShellTreeItem> item =
|
|
childWindows->GetDocShellTreeItemAt(i);
|
|
// This is a bit silly, since we could presumably just do
|
|
// item->GetWindow(). But it's not obvious whether this does the same
|
|
// thing as GetChildWindow() with the item's name (due to the complexity
|
|
// of FindChildWithName). Since GetChildWindow is what we use in
|
|
// getOwnPropDescriptor, let's try to be consistent.
|
|
nsString name;
|
|
item->GetName(name);
|
|
if (!names.Contains(name)) {
|
|
// Make sure we really would expose it from getOwnPropDescriptor.
|
|
RefPtr<BrowsingContext> child = win->GetChildWindow(name);
|
|
if (child && ShouldExposeChildWindow(name, child)) {
|
|
names.AppendElement(name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!AppendNamedPropertyIds(aCx, aProxy, names, false, aProps)) {
|
|
return false;
|
|
}
|
|
|
|
names.Clear();
|
|
Document* doc = win->GetExtantDoc();
|
|
if (!doc || !doc->IsHTMLOrXHTML()) {
|
|
return true;
|
|
}
|
|
nsHTMLDocument* document = doc->AsHTMLDocument();
|
|
// Document names are enumerable, so we want to get them no matter what flags
|
|
// is.
|
|
document->GetSupportedNames(names);
|
|
|
|
JS::RootedVector<jsid> docProps(aCx);
|
|
if (!AppendNamedPropertyIds(aCx, aProxy, names, false, &docProps)) {
|
|
return false;
|
|
}
|
|
|
|
return js::AppendUnique(aCx, aProps, docProps);
|
|
}
|
|
|
|
bool WindowNamedPropertiesHandler::delete_(JSContext* aCx,
|
|
JS::Handle<JSObject*> aProxy,
|
|
JS::Handle<jsid> aId,
|
|
JS::ObjectOpResult& aResult) const {
|
|
return aResult.failCantDeleteWindowNamedProperty();
|
|
}
|
|
|
|
// Note that this class doesn't need any reserved slots, but SpiderMonkey
|
|
// asserts all proxy classes have at least one reserved slot.
|
|
static const DOMIfaceAndProtoJSClass WindowNamedPropertiesClass = {
|
|
PROXY_CLASS_DEF("WindowProperties", JSCLASS_IS_DOMIFACEANDPROTOJSCLASS |
|
|
JSCLASS_HAS_RESERVED_SLOTS(1)),
|
|
eNamedPropertiesObject,
|
|
false,
|
|
prototypes::id::_ID_Count,
|
|
0,
|
|
&sEmptyNativePropertyHooks,
|
|
"[object WindowProperties]",
|
|
EventTarget_Binding::GetProtoObject};
|
|
|
|
// static
|
|
JSObject* WindowNamedPropertiesHandler::Create(JSContext* aCx,
|
|
JS::Handle<JSObject*> aProto) {
|
|
// Note: since the scope polluter proxy lives on the window's prototype
|
|
// chain, it needs a singleton type to avoid polluting type information
|
|
// for properties on the window.
|
|
js::ProxyOptions options;
|
|
options.setSingleton(true);
|
|
options.setClass(&WindowNamedPropertiesClass.mBase);
|
|
|
|
JS::Rooted<JSObject*> gsp(aCx);
|
|
gsp = js::NewProxyObject(aCx, WindowNamedPropertiesHandler::getInstance(),
|
|
JS::NullHandleValue, aProto, options);
|
|
if (!gsp) {
|
|
return nullptr;
|
|
}
|
|
|
|
bool succeeded;
|
|
if (!JS_SetImmutablePrototype(aCx, gsp, &succeeded)) {
|
|
return nullptr;
|
|
}
|
|
MOZ_ASSERT(succeeded,
|
|
"errors making the [[Prototype]] of the named properties object "
|
|
"immutable should have been JSAPI failures, not !succeeded");
|
|
|
|
return gsp;
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|