In SpiderMonkey, a split object is made up of two JSObject
s: an inner object and an outer object.
Each half of a split object always has a pointer to the other half. Inner objects implement the JSExtendedClass.outerObject
hook, which returns a pointer to the corresponding outer object. Outer objects implement the JSExtendedClass.innerObject
hook. But the association is not fixed. An outer object may be associated with different inner objects at different times.
This feature is complicated. Programs other than Mozilla that embed SpiderMonkey should avoid using split objects.
The window
object
Split objects were introduced to resolve a problem posed by the window
object. Three interrelated requirements on the window
object seemed to conflict.
-
The
window
object is the global object for scripts in Web pages. Firefox has to maintain this for Web compatibility. For performance, access to global properties must be fast.
-
There is a glaring difference in the lifetime of the
window
object as seen from two different angles. Suppose a script in page A, in tab TA, has a reference to thewindow
object of page B in another tab TB. Thewindow
object for tab TB must persist as the user navigates from page to page in that tab. To the script in page A, thewindow
must appear to be a single object that moves from page to page. But each web page must load with fresh globals.(The problem is even a bit subtler than this, since the reference may be direct or indirect. The script in A might have opened TB by calling
window.open
, which returns a direct reference to TB'swindow
. More indirectly, suppose there's a JavaScript function defined in page B that refers to global variables in page B. If page A gets a reference to that function, then by calling it, it is indirectly accessing tab TB'swindow
. TheFunction
object carries a reference to thewindow
, via the scope chain. To further complicate matters, A and B are in the same security domain, so security checks between them automatically succeed. This makes the problem resistant to a wrapper-based approach.)Older versions of Firefox accomplished this by blowing away all
window
properties every time a user navigated to another page, then reusing thewindow
. Each page, even on Back, had to load from scratch. This was slow. Circa Firefox 1.5, the decision was made to cache layout information and JavaScript state across navigation. A new approach forwindow
was needed.
-
Security privileges are granted to JS code on the basis of the scope chain of the executing code. SpiderMonkey walks up the scope chain to the nearest object that has
JSPrincipals
attached. The end of the scope chain is the global object for the script. But whatJSPrincipals
should be attached to awindow
object? The principals of the page the window is currently displaying? But then the page that calledwindow.open
could use awith
statement to inject thatwindow
into its scope chain, gaining access to that window's privileges. (Note:with
does add an object to the scope chain, but it's a specialWith
wrapper object that is skipped, or something, during the search described here. This security feature is not peculiar to split objects. ...And yet, there is special code in jsobj.h with a comment about fixing some kind of interaction betweenwith
an split objects. Write that down, it'll be on the test.)
Split objects were the solution. The inner window
object is different for each page a browser window visits. It serves as the "globals" object and provides the JSPrincipals
for scripts on that page. Access to inner window
properties is fast. The outer window
object is the object returned by window.open
. It represents the window or tab itself and survives as the user navigates in that window or tab. The window
object's JSClass.resolve
hook ensures that properties of the inner object are visible via the outer object, if the running code has the right principals to access them. This privilege check may be slow.
See wikimo:Gecko:SplitWindow and bug 296639 for more discussion.
See also https://groups.google.com/group/mozil...81825b338fb84f
Details
This section describes split objects as a feature of the JSAPI. It catalogues (and tries to explain) the subtle ways in which split objects affect SpiderMonkey's behavior.
Split objects are only useful for implementing objects that will be on the scope chain of functions. SpiderMonkey assumes that inner and outer objects are dangerous in two different and complementary ways.
Inner objects are dangerous because they have fast property accessors that do not perform security checks. Therefore, no function may be allowed to see an inner object that has different JSPrincipals
. Apart from the security issue, if one page directly or indirectly gets a reference to another page's window
object, that window
object must appear to behave like the outer window and navigate from page to page. SpiderMonkey arranges this by not allowing JS code to see inner objects at all. To enforce this rule:
- If
this
would evaluate to an inner object, it evaluates to the corresponding outer object instead.
Outer objects are dangerous because their JSPrincipals
may change over time. Because a Function
inherits the JSPrincipals
of its lexical scope (specifically, the nearest principals-aware object in its scope chain), untrusted code must never be able to make an outer object appear in a Function
's scope chain. Again, SpiderMonkey enforces a slightly stronger rule: outer objects may never appear in a scope chain at all, except when put there by an explicit C-level JSAPI call (to JS_SetParent
or equivalent). (Several objects, such as window.location
and window.navigator
, are intentionally parented to the outer window object using such APIs.) To enforce this rule:
- APIs that allow the caller to pass a scope object always check that object first and fail if any outer objects are on its scope chain.
- In the spirit of this rule,
JS_GetScopeChain
should always return an inner object. But there is a special case whenJS_GetScopeChain
is called on aJSContext
in which no code is currently running. By convention, the context's global object is returned in this case. For consistency with the rule, if that global object is an outer object, SpiderMonkey returns the inner object instead.
Inner and outer objects are in certain other respects the same object:
- If
Object.hasOwnProperty
is called on an inner object, and the named property is found on an outer object, that's considered close enough andhasOwnProperty
returnstrue
.
Note that none of the rules listed here affects ordinary property accesses. SpiderMonkey's split object support, by itself, does not cause inner object properties to appear on the outer object or vice versa. Split objects that need this behavior must implement it in custom JSClass
hooks. In the case of window
, each half has custom hooks.