content/html/document/src/HTMLAllCollection.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "mozilla/dom/HTMLAllCollection.h"
michael@0 8
michael@0 9 #include "jsapi.h"
michael@0 10 #include "mozilla/HoldDropJSObjects.h"
michael@0 11 #include "nsContentUtils.h"
michael@0 12 #include "nsDOMClassInfo.h"
michael@0 13 #include "nsHTMLDocument.h"
michael@0 14 #include "nsJSUtils.h"
michael@0 15 #include "nsWrapperCacheInlines.h"
michael@0 16 #include "xpcpublic.h"
michael@0 17
michael@0 18 using namespace mozilla;
michael@0 19 using namespace mozilla::dom;
michael@0 20
michael@0 21 class nsHTMLDocumentSH
michael@0 22 {
michael@0 23 public:
michael@0 24 static bool DocumentAllGetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
michael@0 25 JS::MutableHandle<JS::Value> vp);
michael@0 26 static bool DocumentAllNewResolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
michael@0 27 JS::MutableHandle<JSObject*> objp);
michael@0 28 static void ReleaseDocument(JSFreeOp *fop, JSObject *obj);
michael@0 29 static bool CallToGetPropMapper(JSContext *cx, unsigned argc, JS::Value *vp);
michael@0 30 };
michael@0 31
michael@0 32 const JSClass sHTMLDocumentAllClass = {
michael@0 33 "HTML document.all class",
michael@0 34 JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_NEW_RESOLVE |
michael@0 35 JSCLASS_EMULATES_UNDEFINED,
michael@0 36 JS_PropertyStub, /* addProperty */
michael@0 37 JS_DeletePropertyStub, /* delProperty */
michael@0 38 nsHTMLDocumentSH::DocumentAllGetProperty, /* getProperty */
michael@0 39 JS_StrictPropertyStub, /* setProperty */
michael@0 40 JS_EnumerateStub,
michael@0 41 (JSResolveOp)nsHTMLDocumentSH::DocumentAllNewResolve,
michael@0 42 JS_ConvertStub,
michael@0 43 nsHTMLDocumentSH::ReleaseDocument,
michael@0 44 nsHTMLDocumentSH::CallToGetPropMapper
michael@0 45 };
michael@0 46
michael@0 47 namespace mozilla {
michael@0 48 namespace dom {
michael@0 49
michael@0 50 HTMLAllCollection::HTMLAllCollection(nsHTMLDocument* aDocument)
michael@0 51 : mDocument(aDocument)
michael@0 52 {
michael@0 53 MOZ_ASSERT(mDocument);
michael@0 54 mozilla::HoldJSObjects(this);
michael@0 55 }
michael@0 56
michael@0 57 HTMLAllCollection::~HTMLAllCollection()
michael@0 58 {
michael@0 59 mObject = nullptr;
michael@0 60 mozilla::DropJSObjects(this);
michael@0 61 }
michael@0 62
michael@0 63 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(HTMLAllCollection, AddRef)
michael@0 64 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(HTMLAllCollection, Release)
michael@0 65
michael@0 66 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLAllCollection)
michael@0 67
michael@0 68 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HTMLAllCollection)
michael@0 69 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
michael@0 70 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
michael@0 71 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCollection)
michael@0 72 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNamedMap)
michael@0 73 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 74
michael@0 75 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLAllCollection)
michael@0 76 tmp->mObject = nullptr;
michael@0 77 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
michael@0 78 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCollection)
michael@0 79 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNamedMap)
michael@0 80 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 81
michael@0 82 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(HTMLAllCollection)
michael@0 83 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mObject)
michael@0 84 NS_IMPL_CYCLE_COLLECTION_TRACE_END
michael@0 85
michael@0 86 uint32_t
michael@0 87 HTMLAllCollection::Length()
michael@0 88 {
michael@0 89 return Collection()->Length(true);
michael@0 90 }
michael@0 91
michael@0 92 nsIContent*
michael@0 93 HTMLAllCollection::Item(uint32_t aIndex)
michael@0 94 {
michael@0 95 return Collection()->Item(aIndex);
michael@0 96 }
michael@0 97
michael@0 98 JSObject*
michael@0 99 HTMLAllCollection::GetObject(JSContext* aCx, ErrorResult& aRv)
michael@0 100 {
michael@0 101 MOZ_ASSERT(aCx);
michael@0 102
michael@0 103 if (!mObject) {
michael@0 104 JS::Rooted<JSObject*> wrapper(aCx, mDocument->GetWrapper());
michael@0 105 MOZ_ASSERT(wrapper);
michael@0 106
michael@0 107 JSAutoCompartment ac(aCx, wrapper);
michael@0 108 JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, wrapper));
michael@0 109 mObject = JS_NewObject(aCx, &sHTMLDocumentAllClass, JS::NullPtr(), global);
michael@0 110 if (!mObject) {
michael@0 111 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
michael@0 112 return nullptr;
michael@0 113 }
michael@0 114
michael@0 115 // Make the JSObject hold a reference to the document.
michael@0 116 JS_SetPrivate(mObject, ToSupports(mDocument));
michael@0 117 NS_ADDREF(mDocument);
michael@0 118 }
michael@0 119
michael@0 120 JS::ExposeObjectToActiveJS(mObject);
michael@0 121 return mObject;
michael@0 122 }
michael@0 123
michael@0 124 nsContentList*
michael@0 125 HTMLAllCollection::Collection()
michael@0 126 {
michael@0 127 if (!mCollection) {
michael@0 128 nsIDocument* document = mDocument;
michael@0 129 mCollection = document->GetElementsByTagName(NS_LITERAL_STRING("*"));
michael@0 130 MOZ_ASSERT(mCollection);
michael@0 131 }
michael@0 132 return mCollection;
michael@0 133 }
michael@0 134
michael@0 135 static bool
michael@0 136 DocAllResultMatch(nsIContent* aContent, int32_t aNamespaceID, nsIAtom* aAtom,
michael@0 137 void* aData)
michael@0 138 {
michael@0 139 if (aContent->GetID() == aAtom) {
michael@0 140 return true;
michael@0 141 }
michael@0 142
michael@0 143 nsGenericHTMLElement* elm = nsGenericHTMLElement::FromContent(aContent);
michael@0 144 if (!elm) {
michael@0 145 return false;
michael@0 146 }
michael@0 147
michael@0 148 nsIAtom* tag = elm->Tag();
michael@0 149 if (tag != nsGkAtoms::a &&
michael@0 150 tag != nsGkAtoms::applet &&
michael@0 151 tag != nsGkAtoms::button &&
michael@0 152 tag != nsGkAtoms::embed &&
michael@0 153 tag != nsGkAtoms::form &&
michael@0 154 tag != nsGkAtoms::iframe &&
michael@0 155 tag != nsGkAtoms::img &&
michael@0 156 tag != nsGkAtoms::input &&
michael@0 157 tag != nsGkAtoms::map &&
michael@0 158 tag != nsGkAtoms::meta &&
michael@0 159 tag != nsGkAtoms::object &&
michael@0 160 tag != nsGkAtoms::select &&
michael@0 161 tag != nsGkAtoms::textarea) {
michael@0 162 return false;
michael@0 163 }
michael@0 164
michael@0 165 const nsAttrValue* val = elm->GetParsedAttr(nsGkAtoms::name);
michael@0 166 return val && val->Type() == nsAttrValue::eAtom &&
michael@0 167 val->GetAtomValue() == aAtom;
michael@0 168 }
michael@0 169
michael@0 170 nsContentList*
michael@0 171 HTMLAllCollection::GetDocumentAllList(const nsAString& aID)
michael@0 172 {
michael@0 173 if (nsContentList* docAllList = mNamedMap.GetWeak(aID)) {
michael@0 174 return docAllList;
michael@0 175 }
michael@0 176
michael@0 177 Element* root = mDocument->GetRootElement();
michael@0 178 if (!root) {
michael@0 179 return nullptr;
michael@0 180 }
michael@0 181
michael@0 182 nsCOMPtr<nsIAtom> id = do_GetAtom(aID);
michael@0 183 nsRefPtr<nsContentList> docAllList =
michael@0 184 new nsContentList(root, DocAllResultMatch, nullptr, nullptr, true, id);
michael@0 185 mNamedMap.Put(aID, docAllList);
michael@0 186 return docAllList;
michael@0 187 }
michael@0 188
michael@0 189 nsISupports*
michael@0 190 HTMLAllCollection::GetNamedItem(const nsAString& aID,
michael@0 191 nsWrapperCache** aCache)
michael@0 192 {
michael@0 193 nsContentList* docAllList = GetDocumentAllList(aID);
michael@0 194 if (!docAllList) {
michael@0 195 return nullptr;
michael@0 196 }
michael@0 197
michael@0 198 // Check if there are more than 1 entries. Do this by getting the second one
michael@0 199 // rather than the length since getting the length always requires walking
michael@0 200 // the entire document.
michael@0 201
michael@0 202 nsIContent* cont = docAllList->Item(1, true);
michael@0 203 if (cont) {
michael@0 204 *aCache = docAllList;
michael@0 205 return static_cast<nsINodeList*>(docAllList);
michael@0 206 }
michael@0 207
michael@0 208 // There's only 0 or 1 items. Return the first one or null.
michael@0 209 *aCache = cont = docAllList->Item(0, true);
michael@0 210 return cont;
michael@0 211 }
michael@0 212
michael@0 213 } // namespace dom
michael@0 214 } // namespace mozilla
michael@0 215
michael@0 216 static nsHTMLDocument*
michael@0 217 GetDocument(JSObject *obj)
michael@0 218 {
michael@0 219 MOZ_ASSERT(js::GetObjectJSClass(obj) == &sHTMLDocumentAllClass);
michael@0 220 return static_cast<nsHTMLDocument*>(
michael@0 221 static_cast<nsINode*>(JS_GetPrivate(obj)));
michael@0 222 }
michael@0 223
michael@0 224 bool
michael@0 225 nsHTMLDocumentSH::DocumentAllGetProperty(JSContext *cx, JS::Handle<JSObject*> obj_,
michael@0 226 JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp)
michael@0 227 {
michael@0 228 JS::Rooted<JSObject*> obj(cx, obj_);
michael@0 229
michael@0 230 // document.all.item and .namedItem get their value in the
michael@0 231 // newResolve hook, so nothing to do for those properties here. And
michael@0 232 // we need to return early to prevent <div id="item"> from shadowing
michael@0 233 // document.all.item(), etc.
michael@0 234 if (nsDOMClassInfo::sItem_id == id || nsDOMClassInfo::sNamedItem_id == id) {
michael@0 235 return true;
michael@0 236 }
michael@0 237
michael@0 238 JS::Rooted<JSObject*> proto(cx);
michael@0 239 while (js::GetObjectJSClass(obj) != &sHTMLDocumentAllClass) {
michael@0 240 if (!js::GetObjectProto(cx, obj, &proto)) {
michael@0 241 return false;
michael@0 242 }
michael@0 243
michael@0 244 if (!proto) {
michael@0 245 NS_ERROR("The JS engine lies!");
michael@0 246 return true;
michael@0 247 }
michael@0 248
michael@0 249 obj = proto;
michael@0 250 }
michael@0 251
michael@0 252 HTMLAllCollection* allCollection = GetDocument(obj)->All();
michael@0 253 nsISupports *result;
michael@0 254 nsWrapperCache *cache;
michael@0 255
michael@0 256 if (JSID_IS_STRING(id)) {
michael@0 257 if (nsDOMClassInfo::sLength_id == id) {
michael@0 258 // Make sure <div id="length"> doesn't shadow document.all.length.
michael@0 259 vp.setNumber(allCollection->Length());
michael@0 260 return true;
michael@0 261 }
michael@0 262
michael@0 263 // For all other strings, look for an element by id or name.
michael@0 264 nsDependentJSString str(id);
michael@0 265 result = allCollection->GetNamedItem(str, &cache);
michael@0 266 } else if (JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) {
michael@0 267 // Map document.all[n] (where n is a number) to the n:th item in
michael@0 268 // the document.all node list.
michael@0 269
michael@0 270 nsIContent* node = allCollection->Item(SafeCast<uint32_t>(JSID_TO_INT(id)));
michael@0 271
michael@0 272 result = node;
michael@0 273 cache = node;
michael@0 274 } else {
michael@0 275 result = nullptr;
michael@0 276 }
michael@0 277
michael@0 278 if (result) {
michael@0 279 nsresult rv = nsContentUtils::WrapNative(cx, result, cache, vp);
michael@0 280 if (NS_FAILED(rv)) {
michael@0 281 xpc::Throw(cx, rv);
michael@0 282
michael@0 283 return false;
michael@0 284 }
michael@0 285 } else {
michael@0 286 vp.setUndefined();
michael@0 287 }
michael@0 288
michael@0 289 return true;
michael@0 290 }
michael@0 291
michael@0 292 bool
michael@0 293 nsHTMLDocumentSH::DocumentAllNewResolve(JSContext *cx, JS::Handle<JSObject*> obj,
michael@0 294 JS::Handle<jsid> id,
michael@0 295 JS::MutableHandle<JSObject*> objp)
michael@0 296 {
michael@0 297 JS::Rooted<JS::Value> v(cx);
michael@0 298
michael@0 299 if (nsDOMClassInfo::sItem_id == id || nsDOMClassInfo::sNamedItem_id == id) {
michael@0 300 // Define the item() or namedItem() method.
michael@0 301
michael@0 302 JSFunction *fnc = ::JS_DefineFunctionById(cx, obj, id, CallToGetPropMapper,
michael@0 303 0, JSPROP_ENUMERATE);
michael@0 304 objp.set(obj);
michael@0 305
michael@0 306 return fnc != nullptr;
michael@0 307 }
michael@0 308
michael@0 309 if (nsDOMClassInfo::sLength_id == id) {
michael@0 310 // document.all.length. Any jsval other than undefined would do
michael@0 311 // here, all we need is to get into the code below that defines
michael@0 312 // this propery on obj, the rest happens in
michael@0 313 // DocumentAllGetProperty().
michael@0 314
michael@0 315 v = JSVAL_ONE;
michael@0 316 } else {
michael@0 317 if (!DocumentAllGetProperty(cx, obj, id, &v)) {
michael@0 318 return false;
michael@0 319 }
michael@0 320 }
michael@0 321
michael@0 322 bool ok = true;
michael@0 323
michael@0 324 if (v.get() != JSVAL_VOID) {
michael@0 325 ok = ::JS_DefinePropertyById(cx, obj, id, v, nullptr, nullptr, 0);
michael@0 326 objp.set(obj);
michael@0 327 }
michael@0 328
michael@0 329 return ok;
michael@0 330 }
michael@0 331
michael@0 332 void
michael@0 333 nsHTMLDocumentSH::ReleaseDocument(JSFreeOp *fop, JSObject *obj)
michael@0 334 {
michael@0 335 nsIHTMLDocument* doc = GetDocument(obj);
michael@0 336 if (doc) {
michael@0 337 nsContentUtils::DeferredFinalize(doc);
michael@0 338 }
michael@0 339 }
michael@0 340
michael@0 341 bool
michael@0 342 nsHTMLDocumentSH::CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp)
michael@0 343 {
michael@0 344 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
michael@0 345 // Handle document.all("foo") style access to document.all.
michael@0 346
michael@0 347 if (args.length() != 1) {
michael@0 348 // XXX: Should throw NS_ERROR_XPC_NOT_ENOUGH_ARGS for argc < 1,
michael@0 349 // and create a new NS_ERROR_XPC_TOO_MANY_ARGS for argc > 1? IE
michael@0 350 // accepts nothing other than one arg.
michael@0 351 xpc::Throw(cx, NS_ERROR_INVALID_ARG);
michael@0 352
michael@0 353 return false;
michael@0 354 }
michael@0 355
michael@0 356 // Convert all types to string.
michael@0 357 JS::Rooted<JSString*> str(cx, JS::ToString(cx, args[0]));
michael@0 358 if (!str) {
michael@0 359 return false;
michael@0 360 }
michael@0 361
michael@0 362 // If we are called via document.all(id) instead of document.all.item(i) or
michael@0 363 // another method, use the document.all callee object as self.
michael@0 364 JS::Rooted<JSObject*> self(cx);
michael@0 365 if (args.calleev().isObject() &&
michael@0 366 JS_GetClass(&args.calleev().toObject()) == &sHTMLDocumentAllClass) {
michael@0 367 self = &args.calleev().toObject();
michael@0 368 } else {
michael@0 369 self = JS_THIS_OBJECT(cx, vp);
michael@0 370 if (!self)
michael@0 371 return false;
michael@0 372 }
michael@0 373
michael@0 374 size_t length;
michael@0 375 JS::Anchor<JSString *> anchor(str);
michael@0 376 const jschar *chars = ::JS_GetStringCharsAndLength(cx, str, &length);
michael@0 377 if (!chars) {
michael@0 378 return false;
michael@0 379 }
michael@0 380
michael@0 381 return ::JS_GetUCProperty(cx, self, chars, length, args.rval());
michael@0 382 }

mercurial