Split object

In SpiderMonkey, a split object is made up of two JSObjects: 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 the window object of page B in another tab TB. The window object for tab TB must persist as the user navigates from page to page in that tab. To the script in page A, the window 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's window. 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's window. The Function object carries a reference to the window, 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 the window. 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 for window 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 what JSPrincipals should be attached to a window object? The principals of the page the window is currently displaying? But then the page that called window.open could use a with statement to inject that window 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 special With 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 between with 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 http://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 when JS_GetScopeChain is called on a JSContext 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 and hasOwnProperty returns true.

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.