Bug 786801 - js_InferFlags should not stop at compartment boundaries (r=bhackett)
authorLuke Wagner <luke@mozilla.com>
Tue, 04 Sep 2012 14:31:04 -0700
changeset 107643 adab1fdcfe0a2f336e393883c883b1a2573563e9
parent 107642 4cda8f014413fe052522f9a5962de6a8371e49a8
child 107644 ebd274828b7fb2ce3ffc72043094a295500f0e2f
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersbhackett
bugs786801
milestone18.0a1
Bug 786801 - js_InferFlags should not stop at compartment boundaries (r=bhackett)
content/html/document/test/test_documentAll.html
js/src/jsobj.cpp
--- a/content/html/document/test/test_documentAll.html
+++ b/content/html/document/test/test_documentAll.html
@@ -17,16 +17,18 @@ Tests for document.all
 <div id="content" style="display: none">
   <a id="id1">A</a>
   <a id="id2">B</a>
   <a id="id2">C</a>
   <a id="id3">D</a>
   <a id="id3">E</a>
   <a id="id3">F</a>
 </div>
+<iframe id="subframe" src="data:text/html,<span id='x'></span>"
+        style="display: none"></iframe>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 p = document.getElementById("content");
 
 // Test that several elements with the same id or name behave correctly
 function testNumSame() {
   is(document.all.id0, null, "no ids");
@@ -133,16 +135,24 @@ elementNames.forEach(function (name) {
     hasName.shift();
   }
   else {
     is(document.all[nameval], null, "shouldn't have name");
   }
 });
 is(hasName.length, 0, "found all names");
 
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  var subdoc = $("subframe").contentDocument;
+  is(subdoc.all.x, subdoc.body.firstChild,
+     "document.all should work in a subdocument");
+  SimpleTest.finish();
+});
+
 // Utility functions
 function rC(node) {
   node.parentNode.removeChild(node);
 }
 function testArraysSame(a1, a2) {
   return Array.prototype.every.call(a1, function(e, index) {
     return a2[index] === e;
   }) && a1.length == a2.length;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2398,26 +2398,23 @@ js_CreateThisForFunction(JSContext *cx, 
 }
 
 /*
  * Given pc pointing after a property accessing bytecode, return true if the
  * access is "object-detecting" in the sense used by web scripts, e.g., when
  * checking whether document.all is defined.
  */
 static bool
-Detecting(JSContext *cx, jsbytecode *pc)
+Detecting(JSContext *cx, JSScript *script, jsbytecode *pc)
 {
     /* General case: a branch or equality op follows the access. */
     JSOp op = JSOp(*pc);
     if (js_CodeSpec[op].format & JOF_DETECTING)
         return true;
 
-    JSAtom *atom;
-
-    JSScript *script = cx->stack.currentScript();
     jsbytecode *endpc = script->code + script->length;
     JS_ASSERT(script->code <= pc && pc < endpc);
 
     if (op == JSOP_NULL) {
         /*
          * Special case #1: handle (document.all == null).  Don't sweat
          * about JS1.2's revision of the equality operators here.
          */
@@ -2429,17 +2426,17 @@ Detecting(JSContext *cx, jsbytecode *pc)
     }
 
     if (op == JSOP_GETGNAME || op == JSOP_NAME) {
         /*
          * Special case #2: handle (document.all == undefined).  Don't worry
          * about a local variable named |undefined| shadowing the immutable
          * global binding...because, really?
          */
-        atom = script->getAtom(GET_UINT32_INDEX(pc));
+        JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
         if (atom == cx->names().undefined &&
             (pc += js_CodeSpec[op].length) < endpc) {
             op = JSOp(*pc);
             return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
         }
     }
 
     return false;
@@ -2447,34 +2444,36 @@ Detecting(JSContext *cx, jsbytecode *pc)
 
 /*
  * Infer lookup flags from the currently executing bytecode, returning
  * defaultFlags if a currently executing bytecode cannot be determined.
  */
 unsigned
 js_InferFlags(JSContext *cx, unsigned defaultFlags)
 {
-    const JSCodeSpec *cs;
-    uint32_t format;
+    /*
+     * Use ScriptFrameIter since we intentionally want to look across
+     * compartment boundaries in the case of cross-compartment property access.
+     */
+    ScriptFrameIter i(cx);
+    if (i.done())
+        return defaultFlags;
+
+    jsbytecode *pc = i.pc();
+    JSScript *script = i.script();
+    const JSCodeSpec *cs = &js_CodeSpec[*pc];
+    uint32_t format = cs->format;
     unsigned flags = 0;
-
-    jsbytecode *pc;
-    JSScript *script = cx->stack.currentScript(&pc);
-    if (!script || !pc)
-        return defaultFlags;
-
-    cs = &js_CodeSpec[*pc];
-    format = cs->format;
     if (JOF_MODE(format) != JOF_NAME)
         flags |= JSRESOLVE_QUALIFIED;
     if (format & JOF_SET) {
         flags |= JSRESOLVE_ASSIGNING;
     } else if (cs->length >= 0) {
         pc += cs->length;
-        if (pc < script->code + script->length && Detecting(cx, pc))
+        if (pc < script->code + script->length && Detecting(cx, script, pc))
             flags |= JSRESOLVE_DETECTING;
     }
     return flags;
 }
 
 /* static */ JSBool
 JSObject::nonNativeSetProperty(JSContext *cx, HandleObject obj,
                                HandleId id, MutableHandleValue vp, JSBool strict)
@@ -4325,17 +4324,17 @@ js_GetPropertyHelperInline(JSContext *cx
              * may be called from JS_GetMethodById. See bug 355145.
              */
             if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic))
                 return JS_TRUE;
 
             /* Do not warn about tests like (obj[prop] == undefined). */
             if (cx->resolveFlags == RESOLVE_INFER) {
                 pc += js_CodeSpec[op].length;
-                if (Detecting(cx, pc))
+                if (Detecting(cx, script, pc))
                     return JS_TRUE;
             } else if (cx->resolveFlags & JSRESOLVE_DETECTING) {
                 return JS_TRUE;
             }
 
             unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT;
             cx->stack.currentScript()->warnedAboutUndefinedProp = true;