Merge mozilla-central -> e10s.
authorDan Witte <dwitte@mozilla.com>
Fri, 13 Aug 2010 21:24:11 -0700
changeset 50579 5439a933de49c15e3a3db062fc116762aca41b34
parent 50578 f3d4f656d43994255803474664e29ad1e6ca5218 (current diff)
parent 50557 eccba2835f019af8110fe9127712aa83e04ad5b7 (diff)
child 50580 bfab64ffb0f6b57fb2727de01aa86f293b5a6f51
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0b4pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central -> e10s.
browser/components/sessionstore/test/browser/browser_480893.js
config/autoconf.mk.in
configure.in
js/src/Makefile.in
js/src/configure.in
js/src/jsapi.cpp
modules/libpr0n/decoders/icon/nsIconDecoder.cpp
modules/libpr0n/decoders/icon/nsIconDecoder.h
modules/libpr0n/src/imgContainer.cpp
modules/libpr0n/src/imgContainer.h
modules/libpr0n/src/imgDiscardTracker.cpp
modules/libpr0n/src/imgDiscardTracker.h
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -204,18 +204,18 @@
 #endif
               </menupopup>
             </menu>
 
             <menu id="view-menu" label="&viewMenu.label;"
                   accesskey="&viewMenu.accesskey;">
               <menupopup id="menu_viewPopup">
                 <menuitem id="menu_tabview"
-                          label="&showTabView.label;"
-                          accesskey="&showTabView.accesskey;"
+                          label="&showTabView2.label;"
+                          accesskey="&showTabView2.accesskey;"
                           command="Browser:ToggleTabView"/>
                 <menu id="viewToolbarsMenu"
                       label="&viewToolbarsMenu.label;"
                       accesskey="&viewToolbarsMenu.accesskey;">
                   <menupopup onpopupshowing="onViewToolbarsPopupShowing(event);">
                     <menuseparator/>
                     <menuitem id="menu_tabsOnTop"
                               command="cmd_ToggleTabsOnTop"
--- a/browser/base/content/browser-tabview.js
+++ b/browser/base/content/browser-tabview.js
@@ -41,17 +41,17 @@ let TabView = {
   _sessionstore: null,
   _visibilityID: "tabview-visibility",
   
   // ----------
   get windowTitle() {
     delete this.windowTitle;
     let brandBundle = document.getElementById("bundle_brand");
     let brandShortName = brandBundle.getString("brandShortName");
-    let title = gNavigatorBundle.getFormattedString("tabView.title", [brandShortName]);
+    let title = gNavigatorBundle.getFormattedString("tabView2.title", [brandShortName]);
     return this.windowTitle = title;
   },
 
   // ----------
   init: function TabView_init() {    
     // ___ keys    
     this._setBrowserKeyHandlers();
 
@@ -192,18 +192,44 @@ let TabView = {
       let charCode = event.charCode;
 #ifdef XP_MACOSX
       // if a text box in a webpage has the focus, the event.altKey would
       // return false so we are depending on the charCode here.
       if (!event.ctrlKey && !event.metaKey && !event.shiftKey &&
           charCode == 160) { // alt + space
 #else
       if (event.ctrlKey && !event.metaKey && !event.shiftKey &&
-          event.altKey && charCode == 32) { // ctrl + alt + space
+          !event.altKey && charCode == 32) { // ctrl + space
 #endif
+
+        // Don't handle this event if it's coming from a node that might allow
+        // multiple keyboard selection like selects or trees
+        let node = event.target;
+        switch (node.namespaceURI) {
+          case "http://www.w3.org/1999/xhtml":
+            // xhtml:select only allows multiple when the attr is set
+            if (node.localName == "select" && node.hasAttribute("multiple"))
+              return;
+            break;
+
+          case "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul":
+            switch (node.localName) {
+              case "listbox":
+                // xul:listbox is by default single
+                if (node.getAttribute("seltype") == "multiple")
+                  return;
+                break;
+              case "tree":
+                // xul:tree is by default multiple
+                if (node.getAttribute("seltype") != "single")
+                  return;
+                break;
+            }
+        }
+
         event.stopPropagation();
         event.preventDefault();
         self.show();
         return;
       }
 
       // Control (+ Shift) + `
       if (event.ctrlKey && !event.metaKey && !event.altKey &&
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5838,25 +5838,22 @@ function WindowIsClosing()
  * @returns true if closing can proceed, false if it got cancelled.
  */
 function warnAboutClosingWindow() {
   // Popups aren't considered full browser windows.
   if (!toolbar.visible)
     return gBrowser.warnAboutClosingTabs(true);
 
   // Figure out if there's at least one other browser window around.
-  let foundOtherBrowserWindow = false;
   let e = Services.wm.getEnumerator("navigator:browser");
-  while (e.hasMoreElements() && !foundOtherBrowserWindow) {
+  while (e.hasMoreElements()) {
     let win = e.getNext();
     if (win != window && win.toolbar.visible)
-      foundOtherBrowserWindow = true;
-  }
-  if (foundOtherBrowserWindow)
-    return gBrowser.warnAboutClosingTabs(true);
+      return gBrowser.warnAboutClosingTabs(true);
+  }
 
   let os = Services.obs;
 
   let closingCanceled = Cc["@mozilla.org/supports-PRBool;1"].
                         createInstance(Ci.nsISupportsPRBool);
   os.notifyObservers(closingCanceled,
                      "browser-lastwindow-close-requested", null);
   if (closingCanceled.data)
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -241,17 +241,16 @@
            onMozMousePixelScroll="InspectorUI.highlighter.handlePixelScroll(event);"/>
 
     <panel id="inspector-panel"
            orient="vertical"
            hidden="true"
            ignorekeys="true"
            noautofocus="true"
            noautohide="true"
-           level="top"
            titlebar="normal"
            label="&inspectPanelTitle.label;">
       <toolbar id="inspector-toolbar"
                nowindowdrag="true">
         <toolbarbutton id="inspector-inspect-toolbutton"
                        label="&inspectButton.label;"
                        accesskey="&inspectButton.accesskey;"
                        class="toolbarbutton-text"
@@ -293,17 +292,16 @@
     </panel>
 
     <panel id="inspector-style-panel"
            hidden="true"
            orient="vertical"
            ignorekeys="true"
            noautofocus="true"
            noautohide="true"
-           level="top"
            titlebar="normal"
            label="&inspectStylePanelTitle.label;">
         <listbox id="inspector-style-listbox" flex="1"/>
         <hbox align="end">
           <spacer flex="1" />
           <resizer dir="bottomend" />
         </hbox>
     </panel>
@@ -938,19 +936,19 @@
                      label="&listAllTabs.label;"
                      tooltiptext="&listAllTabs.label;"
                      removable="true">
         <menupopup id="alltabs-popup"
                    position="after_end"/>
       </toolbarbutton>
 
       <toolbarbutton id="tabview-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                     label="&tabViewButton.label;"
+                     label="&tabViewButton2.label;"
                      command="Browser:ToggleTabView"
-                     tooltiptext="&tabViewButton.tooltip;"
+                     tooltiptext="&tabViewButton2.tooltip;"
                      removable="true"/>
 
       <toolbarbutton id="tabs-closebutton"
                      class="close-button tabs-closebutton"
                      command="cmd_close"
                      label="&closeTab.label;"
                      tooltiptext="&closeTab.label;"/>
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -505,19 +505,22 @@
 
                 // Don't clear the favicon if this onLocationChange was
                 // triggered by a pushState or a replaceState.  See bug 550565.
                 if (aWebProgress.isLoadingDocument &&
                     !(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE))
                   this.mBrowser.mIconURL = null;
 
                 let browserHistory = this.mTabBrowser.mBrowserHistory;
-                if (this.mBrowser.lastURI)
-                  browserHistory.unregisterOpenPage(this.mBrowser.lastURI);
+                if (this.mBrowser.registeredOpenURI) {
+                  browserHistory.unregisterOpenPage(this.mBrowser.registeredOpenURI);
+                  delete this.mBrowser.registeredOpenURI;
+                }
                 browserHistory.registerOpenPage(aLocation);
+                this.mBrowser.registeredOpenURI = aLocation;
               }
 
               if (!this.mBlank) {
                 this._callProgressListeners("onLocationChange",
                                             [aWebProgress, aRequest, aLocation]);
               }
 
               if (topLevel)
@@ -718,17 +721,17 @@
             return newTitle;
           ]]>
         </body>
       </method>
 
       <method name="updateTitlebar">
         <body>
           <![CDATA[
-            if (TabView && TabView.isVisible()) {
+            if (window.TabView && TabView.isVisible()) {
               // ToDo: this will be removed when we gain ability to draw to the menu bar.
               // Bug 586175
               this.ownerDocument.title = TabView.windowTitle;
             } else {
               this.ownerDocument.title = this.getWindowTitleForBrowser(this.mCurrentBrowser);
             }
           ]]>
         </body>
@@ -1245,20 +1248,18 @@
           ]]>
         </body>
       </method>
 
       <method name="warnAboutClosingTabs">
       <parameter name="aAll"/>
       <body>
         <![CDATA[
-          var tabsToClose = this.tabs.length;
-
-          if (!aAll)
-            tabsToClose = this.visibleTabs.length - (1 + this._numPinnedTabs);
+          var tabsToClose = (aAll ? this.tabs.length : this.visibleTabs.length - 1)
+                            - gBrowser._numPinnedTabs;
           if (tabsToClose <= 1)
             return true;
 
           const pref = "browser.tabs.warnOnClose";
           var shouldPrompt = Services.prefs.getBoolPref(pref);
 
           if (!shouldPrompt)
             return true;
@@ -1395,18 +1396,17 @@
             if (!aTabWillBeMoved) {
               let ds = browser.docShell;
               if (ds && ds.contentViewer && !ds.contentViewer.permitUnload())
                 return false;
             }
 
             var closeWindow = false;
             var newTab = false;
-            if (this.tabs.length - this._removingTabs.length == 1 && 
-                (!TabView || !TabView.isVisible())) {
+            if (this.tabs.length - this._removingTabs.length == 1) {
               closeWindow = aCloseWindowWithLastTab != null ? aCloseWindowWithLastTab :
                             !window.toolbar.visible ||
                               this.tabContainer._closeWindowWithLastTab;
 
               // Closing the tab and replacing it with a blank one is notably slower
               // than closing the window right away. If the caller opts in, take
               // the fast path.
               if (closeWindow &&
@@ -1431,18 +1431,20 @@
             aTab.dispatchEvent(evt);
 
             // Remove the tab's filter and progress listener.
             const filter = this.mTabFilters[aTab._tPos];
             browser.webProgress.removeProgressListener(filter);
             filter.removeProgressListener(this.mTabListeners[aTab._tPos]);
             this.mTabListeners[aTab._tPos].destroy();
 
-            if (browser.currentURI)
-              this.mBrowserHistory.unregisterOpenPage(browser.currentURI);
+            if (browser.registeredOpenURI) {
+              this.mBrowserHistory.unregisterOpenPage(browser.registeredOpenURI);
+              delete browser.registeredOpenURI;
+            }
 
             // We are no longer the primary content area.
             browser.setAttribute("type", "content-targetable");
 
             // Remove this tab as the owner of any other tabs, since it's going away.
             Array.forEach(this.tabs, function (tab) {
               if ("owner" in tab && tab.owner == aTab)
                 // |tab| is a child of the tab we're removing, make it an orphan
@@ -2307,17 +2309,20 @@
           this.mCurrentBrowser.droppedLinkHandler = handleDroppedLink;
         ]]>
       </constructor>
 
       <destructor>
         <![CDATA[
           for (var i = 0; i < this.mTabListeners.length; ++i) {
             let browser = this.getBrowserAtIndex(i);
-            this.mBrowserHistory.unregisterOpenPage(browser.currentURI);
+            if (browser.registeredOpenURI) {
+              this.mBrowserHistory.unregisterOpenPage(browser.registeredOpenURI);
+              delete browser.registeredOpenURI;
+            }
             browser.webProgress.removeProgressListener(this.mTabFilters[i]);
             this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
             this.mTabFilters[i] = null;
             this.mTabListeners[i].destroy();
             this.mTabListeners[i] = null;
           }
           document.removeEventListener("keypress", this, false);
         ]]>
--- a/browser/base/content/tabview/drag.js
+++ b/browser/base/content/tabview/drag.js
@@ -103,18 +103,18 @@ Drag.prototype = {
   //   bounds             - (<Rect>) bounds
   //   stationaryCorner   - which corner is stationary? by default, the top left.
   //                        "topleft", "bottomleft", "topright", "bottomright"
   //   assumeConstantSize - (boolean) whether the bounds' dimensions are sacred or not.
   //   keepProportional   - (boolean) if assumeConstantSize is false, whether we should resize
   //                        proportionally or not
   //   checkItemStatus    - (boolean) make sure this is a valid item which should be snapped
   snapBounds: function Drag_snapBounds(bounds, stationaryCorner, assumeConstantSize, keepProportional, checkItemStatus) {
-		if (!stationaryCorner)
-			stationaryCorner || 'topleft';
+    if (!stationaryCorner)
+      stationaryCorner || 'topleft';
     var update = false; // need to update
     var updateX = false;
     var updateY = false;
     var newRect;
     var snappedTrenches = {};
 
     // OH SNAP!
 
--- a/browser/base/content/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -121,25 +121,23 @@ let GroupItem = function GroupItem(listO
 
   this.isDragging = false;
   $container
     .css({zIndex: -100})
     .appendTo("body");
 
   // ___ New Tab Button
   this.$ntb = iQ("<div>")
-    .appendTo($container);
-
-  this.$ntb
     .addClass('newTabButton')
     .click(function() {
       self.newTab();
-    });
-
-  (this.$ntb)[0].title = 'New tab';
+    })
+    .attr('title',
+          "New tab")
+    .appendTo($container);
 
   // ___ Resizer
   this.$resizer = iQ("<div>")
     .addClass('resizer')
     .appendTo($container)
     .hide();
 
   // ___ Titlebar
@@ -164,17 +162,17 @@ let GroupItem = function GroupItem(listO
       self.closeAll();
     })
     .appendTo($container);
 
   // ___ Title
   this.$titleContainer = iQ('.title-container', this.$titlebar);
   this.$title = iQ('.name', this.$titlebar);
   this.$titleShield = iQ('.title-shield', this.$titlebar);
-  this.setTitle(options.title || "");
+  this.setTitle(options.title || this.defaultName);
 
   var titleUnfocus = function() {
     self.$titleShield.show();
     if (!self.getTitle()) {
       self.$title
         .addClass("defaultName")
         .val(self.defaultName);
     } else {
@@ -293,17 +291,17 @@ let GroupItem = function GroupItem(listO
   }
 };
 
 // ----------
 window.GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
   // ----------
   // Variable: defaultName
   // The prompt text for the title field.
-  defaultName: "name this groupItem...",
+  defaultName: "Name this tab group…",
 
   // -----------
   // Function: setActiveTab
   // Sets the active <TabItem> for this groupItem
   setActiveTab: function(tab) {
     Utils.assert(tab && tab.isATabItem, 'tab must be a TabItem');
     this._activeTab = tab;
   },
@@ -369,17 +367,17 @@ window.GroupItem.prototype = Utils.exten
   },
 
   // ----------
   // Function: adjustTitleSize
   // Used to adjust the width of the title box depending on groupItem width and title size.
   adjustTitleSize: function() {
     Utils.assert(this.bounds, 'bounds needs to have been set');
     let closeButton = iQ('.close', this.container);
-    var w = Math.min(this.bounds.width - closeButton.width() - closeButton.css('right'),
+    var w = Math.min(this.bounds.width - parseInt(closeButton.width()) - parseInt(closeButton.css('right')),
                      Math.max(150, this.getTitle().length * 6));
     // The * 6 multiplier calculation is assuming that characters in the title
     // are approximately 6 pixels wide. Bug 586545
     var css = {width: w};
     this.$title.css(css);
     this.$titleShield.css(css);
   },
 
@@ -650,17 +648,20 @@ window.GroupItem.prototype = Utils.exten
         if (typeof item.setResizable == 'function')
           item.setResizable(false);
 
         if (item.tab == gBrowser.selectedTab)
           GroupItems.setActiveGroupItem(this);
       }
 
       if (!options.dontArrange) {
-        this.arrange();
+        // by default, we animate the item moving to its new position
+        let animate = typeof options.animate == "undefined" ? true :
+                      options.animate;
+        this.arrange({ animate: animate });
       }
       UI.setReorderTabsOnHide(this);
 
       if (this._nextNewTabCallback) {
         this._nextNewTabCallback.apply(this, [item])
         this._nextNewTabCallback = null;
       }
     } catch(e) {
@@ -1792,16 +1793,17 @@ window.GroupItems = {
       UI.showTabView();
     }
   },
 
   // ----------
   // Function: killNewTabGroup
   // Removes the New Tab Group, which is now defunct. See bug 575851 and comments therein.
   killNewTabGroup: function() {
+    let newTabGroupTitle = "New Tabs";
     this.groupItems.forEach(function(groupItem) {
-      if (groupItem.getTitle() == 'New Tabs' && groupItem.locked.title) {
+      if (groupItem.getTitle() == newTabGroupTitle && groupItem.locked.title) {
         groupItem.removeAll();
         groupItem.close();
       }
     });
   }
 };
--- a/browser/base/content/tabview/iq.js
+++ b/browser/base/content/tabview/iq.js
@@ -526,19 +526,17 @@ iQClass.prototype = {
   //     in, but "this" is set to the element that was animated.
   animate: function(css, options) {
     Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
 
     if (!options)
       options = {};
 
     let easings = {
-      tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.0)", 
-      // TODO: change 1.0 above to 1.29 after bug 575672 is fixed
-      
+      tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.29)", 
       easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care
       fast: 'cubic-bezier(0.7,0,1,1)'
     };
 
     let duration = (options.duration || 400);
     let easing = (easings[options.easing] || 'ease');
 
     // The latest versions of Firefox do not animate from a non-explicitly
--- a/browser/base/content/tabview/items.js
+++ b/browser/base/content/tabview/items.js
@@ -1043,17 +1043,17 @@ window.Items = {
         var blocked = false;
         pairs.forEach(function(pair2) {
           if (pair2 == pair || pair2.item == ignore)
             return;
 
           var bounds2 = pair2.bounds;
           if (bounds2.intersects(newBounds))
             blocked = true;
-					return;
+          return;
         });
 
         if (!blocked) {
           pair.bounds.copy(newBounds);
         }
       }
       return;
     });
--- a/browser/base/content/tabview/tabview.css
+++ b/browser/base/content/tabview/tabview.css
@@ -342,36 +342,36 @@ input.name {
   border: 1px solid transparent;
   color: #999;
   margin: 3px 0px 0px 3px;
   padding: 1px;
   background-image: url(chrome://browser/skin/tabview/edit-light.png);
   padding-left: 20px;
 }
 
-input.name:hover {
+.title-container:hover input.name {
   border: 1px solid #ddd;
 }
 
-input.name-locked:hover {
+.title-container:hover input.name-locked {
   border: 1px solid transparent !important;
   cursor: default;
 }
 
 input.name:focus {
   color: #555;
 }
 
 input.defaultName {
   font-style: italic !important;
   background-image-opacity: .1;
   color: transparent;
 }
 
-input.defaultName:hover {
+.title-container:hover input.defaultName {
   color: #CCC;
 }
 
 .title-container {
   cursor: text;
 }
 
 .title-shield {
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -102,17 +102,22 @@ var UIManager = {
       gWindow.addEventListener("tabviewshow", function() {
         self.showTabView(true);
       }, false);
 
       // ___ currentTab
       this._currentTab = gBrowser.selectedTab;
 
       // ___ Dev Menu
-      this._addDevMenu();
+      // This dev menu is not meant for shipping, nor is it of general
+      // interest, but we still need it for the time being. Change the 
+      // false below to enable; just remember to change back before 
+      // committing. Bug 586721 will track the ultimate removal. 
+      if (false)
+        this._addDevMenu();
 
       // When you click on the background/empty part of TabView,
       // we create a new groupItem.
       iQ(gTabViewFrame.contentDocument).mousedown(function(e) {
         if (iQ(":focus").length > 0) {
           iQ(":focus").each(function(element) {
             if (element.nodeName == "INPUT")
               element.blur();
@@ -148,16 +153,20 @@ var UIManager = {
       GroupItems.init();
 
       var groupItemsData = Storage.readGroupItemsData(gWindow);
       var firstTime = !groupItemsData || Utils.isEmptyObject(groupItemsData);
       var groupItemData = Storage.readGroupItemData(gWindow);
       GroupItems.reconstitute(groupItemsData, groupItemData);
       GroupItems.killNewTabGroup(); // temporary?
 
+      // ___ tabs
+      TabItems.init();
+      TabItems.pausePainting();
+
       if (firstTime) {
         var padding = 10;
         var infoWidth = 350;
         var infoHeight = 350;
         var pageBounds = Items.getPageBounds();
         pageBounds.inset(padding, padding);
 
         // ___ make a fresh groupItem
@@ -171,39 +180,38 @@ var UIManager = {
 
         var groupItem = new GroupItem([], options);
 
         var items = TabItems.getItems();
         items.forEach(function(item) {
           if (item.parent)
             item.parent.remove(item);
 
-          groupItem.add(item);
+          groupItem.add(item, null, {animate: false});
         });
 
         // ___ make info item
+        let welcome = "How to organize your tabs";
+        let more = "";
+        let video = "http://videos-cdn.mozilla.net/firefox4beta/tabcandy_howto.webm";
         var html =
           "<div class='intro'>"
-            + "<h1>Welcome to Firefox Tab Sets</h1>" // TODO: This needs to be localized if it's kept in
-            + "<div>(more goes here)</div><br>"
-            + "<video src='http://people.mozilla.org/~araskin/movies/tabcandy_howto.webm' "
+            + "<h1>" + welcome + "</h1>"
+            + ( more && more.length ? "<div>" + more + "</div><br>" : "")
+            + "<video src='" + video + "' "
             + "width='100%' preload controls>"
           + "</div>";
 
         box.left = box.right + padding;
         box.width = infoWidth;
         box.height = infoHeight;
         var infoItem = new InfoItem(box);
         infoItem.html(html);
       }
 
-      // ___ tabs
-      TabItems.init();
-      TabItems.pausePainting();
-
       // ___ resizing
       if (this._pageBounds)
         this._resize(true);
       else
         this._pageBounds = Items.getPageBounds();
 
       iQ(window).resize(function() {
         self._resize();
@@ -299,17 +307,17 @@ var UIManager = {
 
     this._reorderTabItemsOnShow.forEach(function(groupItem) {
       groupItem.reorderTabItemsBasedOnTabOrder();
     });
     this._reorderTabItemsOnShow = [];
 
 #ifdef XP_WIN
     // Restore the full height when showing TabView
-    gTabViewFrame.style.marginTop = 0;
+    gTabViewFrame.style.marginTop = "22px";
 #endif
     gTabViewDeck.selectedIndex = 1;
     gTabViewFrame.contentWindow.focus();
 
     gBrowser.updateTitlebar();
 #ifdef XP_MACOSX
     this._setActiveTitleColor(true);
 #endif
@@ -631,17 +639,17 @@ var UIManager = {
         event.preventDefault();
       } else if (event.which == 32) {
         // alt/control + space to zoom into the active tab.
 #ifdef XP_MACOSX
         if (event.altKey && !event.metaKey && !event.shiftKey &&
             !event.ctrlKey) {
 #else
         if (event.ctrlKey && !event.metaKey && !event.shiftKey &&
-            event.altKey) {
+            !event.altKey) {
 #endif
           var activeTab = self.getActiveTab();
           if (activeTab)
             activeTab.zoomIn();
           event.stopPropagation();
           event.preventDefault();
         }
       } else if (event.which == 27 || event.which == 13) {
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -65,17 +65,16 @@ include $(topsrcdir)/config/rules.mk
 		test_bug364677.html \
 		bug364677-data.xml \
 		bug364677-data.xml^headers^ \
 		test_offline_gzip.html \
 		gZipOfflineChild.html \
 		gZipOfflineChild.html^headers^ \
 		gZipOfflineChild.cacheManifest \
 		gZipOfflineChild.cacheManifest^headers^ \
-		test_bug452451.html \
 		$(NULL)
 
 ifeq (,$(filter gtk2,$(MOZ_WIDGET_TOOLKIT)))
 _TEST_FILES += \
 		test_contextmenu.html \
 		subtst_contextmenu.html \
 		$(NULL)
 
--- a/browser/base/content/test/browser_tabMatchesInAwesomebar.js
+++ b/browser/base/content/test/browser_tabMatchesInAwesomebar.js
@@ -32,17 +32,16 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/Services.jsm");
 
 const TEST_URL_BASES = [
   "http://example.org/browser/browser/base/content/test/dummy_page.html#tabmatch",
   "http://example.org/browser/browser/base/content/test/moz.png#tabmatch"
 ];
 
 var gPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
                          getService(Ci.nsIPrivateBrowsingService);
@@ -114,43 +113,60 @@ var gTestSteps = [
           tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
             ensure_opentabs_match_db();
             nextStep()
         }, true);
         tab.linkedBrowser.contentDocument.querySelector("iframe").src = "http://test2.example.org/";
       });
     }, true);
     tab.linkedBrowser.loadURI('data:text/html,<body><iframe src=""></iframe></body>');
-  }
+  },
+  function() {
+    info("Running step 7 - remove tab immediately");
+    let tab = gBrowser.addTab("about:blank");
+    gBrowser.removeTab(tab);
+    ensure_opentabs_match_db();
+    nextStep();
+  },
 ];
 
 
 
 function test() {
   waitForExplicitFinish();
   nextStep();
 }
 
 function loadTab(tab, url) {
+  // Because adding visits is async, we will not be notified immediately.
+  let visited = false;
+
   tab.linkedBrowser.addEventListener("load", function (event) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
-    if (--gTabWaitCount > 0)
-      return;
-    is(gTabWaitCount, 0,
-       "sanity check, gTabWaitCount should not be decremented below 0");
+    Services.obs.addObserver(
+      function (aSubject, aTopic, aData) {
+        if (url != aSubject.QueryInterface(Ci.nsIURI).spec)
+          return;
+        Services.obs.removeObserver(arguments.callee, aTopic);
+        if (--gTabWaitCount > 0)
+          return;
+        is(gTabWaitCount, 0,
+           "sanity check, gTabWaitCount should not be decremented below 0");
 
-    try {
-      ensure_opentabs_match_db();
-    } catch (e) {
-      ok(false, "exception from ensure_openpages_match_db: " + e);
-    }
+        try {
+          ensure_opentabs_match_db();
+        } catch (e) {
+          ok(false, "exception from ensure_openpages_match_db: " + e);
+        }
 
-    executeSoon(nextStep);
+        executeSoon(nextStep);
+      }, "uri-visit-saved", false);
   }, true);
+
   gTabWaitCount++;
   tab.linkedBrowser.loadURI(url);
 }
 
 function nextStep() {
   if (gTestSteps.length == 0) {
     while (gBrowser.tabs.length > 1) {
       gBrowser.selectTabAtIndex(1);
@@ -186,20 +202,20 @@ function ensure_opentabs_match_db() {
     }
   }
 
   var db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
                               .DBConnection;
 
   try {
     var stmt = db.createStatement(
-                          "SELECT IFNULL(p_t.url, p.url) AS url, open_count, place_id " +
-                          "FROM moz_openpages_temp " +
-                          "LEFT JOIN moz_places p ON p.id=place_id " +
-                          "LEFT JOIN moz_places_temp p_t ON p_t.id=place_id");
+                          "SELECT t.url, open_count, IFNULL(p_t.id, p.id) " +
+                          "FROM moz_openpages_temp t " +
+                          "LEFT JOIN moz_places p ON p.url = t.url " +
+                          "LEFT JOIN moz_places_temp p_t ON p_t.url = t.url");
   } catch (e) {
     ok(false, "error creating db statement: " + e);
     return;
   }
 
   var dbtabs = [];
   try {
     while (stmt.executeStep()) {
@@ -211,29 +227,21 @@ function ensure_opentabs_match_db() {
          "(" + tabs[stmt.row.url] + "): " + stmt.row.url);
       dbtabs.push(stmt.row.url);
     }
   } finally {
     stmt.finalize();
   }
 
   for (let url in tabs) {
-    // ignore URLs that should never be in the places db
-    if (!is_expected_in_db(url))
-      continue;
     ok(dbtabs.indexOf(url) > -1,
        "tab is open (" + tabs[url] + " times) and should recorded in db: " + url);
   }
 }
 
-function is_expected_in_db(url) {
-  var uri = Services.io.newURI(url, null, null);
-  return PlacesUtils.history.canAddURI(uri);
-}
-
 /**
  * Clears history invoking callback when done.
  */
 function waitForClearHistory(aCallback) {
   const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
   let observer = {
     observe: function(aSubject, aTopic, aData) {
       Services.obs.removeObserver(this, TOPIC_EXPIRATION_FINISHED);
--- a/browser/base/content/test/tabview/browser_tabview_launch.js
+++ b/browser/base/content/test/tabview/browser_tabview_launch.js
@@ -51,17 +51,17 @@ function test() {
         var keyCode = 0;
         var charCode;
         var eventObject;
         if (navigator.platform.indexOf("Mac") != -1) {
           charCode = 160;
           eventObject = { altKey: true };
         } else {
           charCode = 32;
-          eventObject = { altKey: true, ctrlKey: true };
+          eventObject = { ctrlKey: true };
         }
         var modifiers = EventUtils._parseModifiers(eventObject);
         var keyDownDefaultHappened =
             utils.sendKeyEvent("keydown", keyCode, charCode, modifiers);
         utils.sendKeyEvent("keypress", keyCode, charCode, modifiers,
                              !keyDownDefaultHappened);
         utils.sendKeyEvent("keyup", keyCode, charCode, modifiers);
       }
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -433,17 +433,17 @@ BrowserGlue.prototype = {
     var pagecount = 0;
     var browserEnum = Services.wm.getEnumerator("navigator:browser");
     while (browserEnum.hasMoreElements()) {
       windowcount++;
 
       var browser = browserEnum.getNext();
       var tabbrowser = browser.document.getElementById("content");
       if (tabbrowser)
-        pagecount += tabbrowser.browsers.length;
+        pagecount += tabbrowser.browsers.length - tabbrowser._numPinnedTabs;
     }
 
     this._saveSession = false;
     if (pagecount < 2)
       return;
 
     if (aQuitType != "restart")
       aQuitType = "quit";
@@ -464,17 +464,17 @@ BrowserGlue.prototype = {
         showPrompt = Services.prefs.getBoolPref("browser.tabs.warnOnClose");
     } catch (ex) {}
 
     // Never show a prompt inside the private browsing mode
     var inPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
                             getService(Ci.nsIPrivateBrowsingService).
                             privateBrowsingEnabled;
     if (!showPrompt || inPrivateBrowsing)
-      return false;
+      return;
 
     var quitBundle = Services.strings.createBundle("chrome://browser/locale/quitDialog.properties");
     var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
 
     var appName = brandBundle.GetStringFromName("brandShortName");
     var quitDialogTitle = quitBundle.formatStringFromName(aQuitType + "DialogTitle",
                                                           [appName], 1);
 
--- a/browser/components/sessionstore/content/aboutSessionRestore.js
+++ b/browser/components/sessionstore/content/aboutSessionRestore.js
@@ -138,24 +138,16 @@ function restoreSession() {
     ss.setWindowState(newWindow, stateString, true);
     
     var tabbrowser = top.gBrowser;
     var tabIndex = tabbrowser.getBrowserIndexForDocument(document);
     tabbrowser.removeTab(tabbrowser.tabs[tabIndex]);
   }, true);
 }
 
-function startNewSession() {
-  var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
-  if (prefBranch.getIntPref("browser.startup.page") == 0)
-    getBrowserWindow().gBrowser.loadURI("about:blank");
-  else
-    getBrowserWindow().BrowserHome();
-}
-
 function onListClick(aEvent) {
   // don't react to right-clicks
   if (aEvent.button == 2)
     return;
   
   var row = {}, col = {};
   treeView.treeBox.getCellAt(aEvent.clientX, aEvent.clientY, row, col, {});
   if (col.value) {
--- a/browser/components/sessionstore/content/aboutSessionRestore.xhtml
+++ b/browser/components/sessionstore/content/aboutSessionRestore.xhtml
@@ -98,30 +98,18 @@
             </treecols>
             <treechildren flex="1"/>
           </tree>
         </div>
       </div>
 
       <!-- Buttons -->
       <hbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="buttons">
-#ifdef XP_UNIX
-        <button id="errorCancel" label="&restorepage.cancelButton;"
-                accesskey="&restorepage.cancel.access;"
-                oncommand="startNewSession();"/>
         <button id="errorTryAgain" label="&restorepage.tryagainButton;"
                 accesskey="&restorepage.restore.access;"
                 oncommand="restoreSession();"/>
-#else
-        <button id="errorTryAgain" label="&restorepage.tryagainButton;"
-                accesskey="&restorepage.restore.access;"
-                oncommand="restoreSession();"/>
-        <button id="errorCancel" label="&restorepage.cancelButton;"
-                accesskey="&restorepage.cancel.access;"
-                oncommand="startNewSession();"/>
-#endif
       </hbox>
       <!-- holds the session data for when the tab is closed -->
       <input type="text" id="sessionData" style="display: none;"/>
     </div>
 
   </body>
 </html>
--- a/browser/components/sessionstore/jar.mn
+++ b/browser/components/sessionstore/jar.mn
@@ -1,3 +1,3 @@
 browser.jar:
-*   content/browser/aboutSessionRestore.xhtml             (content/aboutSessionRestore.xhtml) 
+    content/browser/aboutSessionRestore.xhtml             (content/aboutSessionRestore.xhtml) 
 *   content/browser/aboutSessionRestore.js                (content/aboutSessionRestore.js)
--- a/browser/components/sessionstore/src/nsSessionStartup.js
+++ b/browser/components/sessionstore/src/nsSessionStartup.js
@@ -152,17 +152,24 @@ SessionStartup.prototype = {
       this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
     else if (!lastSessionCrashed && doResumeSession)
       this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
     else
       this._iniString = null; // reset the state string
 
     if (this._sessionType != Ci.nsISessionStartup.NO_SESSION) {
       // wait for the first browser window to open
-      Services.obs.addObserver(this, "domwindowopened", true);
+
+      // Don't reset the initial window's default args (i.e. the home page(s))
+      // if all stored tabs are pinned.
+      if (!initialState.windows ||
+          !initialState.windows.every(function (win)
+             win.tabs.every(function (tab) tab.pinned)))
+        Services.obs.addObserver(this, "domwindowopened", true);
+
       Services.obs.addObserver(this, "browser:purge-session-history", true);
     }
   },
 
   /**
    * Handle notifications
    */
   observe: function sss_observe(aSubject, aTopic, aData) {
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -193,20 +193,18 @@ SessionStoreService.prototype = {
   _recentCrashes: 0,
 
   // whether we are in private browsing mode
   _inPrivateBrowsing: false,
 
   // whether we clearing history on shutdown
   _clearingOnShutdown: false,
 
-#ifndef XP_MACOSX
   // whether the last window was closed and should be restored
   _restoreLastWindow: false,
-#endif
 
 /* ........ Global Event Handlers .............. */
 
   /**
    * Initialize the component
    */
   init: function sss_init(aWindow) {
     if (!aWindow || this._loadState == STATE_RUNNING) {
@@ -285,20 +283,18 @@ SessionStoreService.prototype = {
         // make sure that at least the first window doesn't have anything hidden
         delete this._initialState.windows[0].hidden;
         // Since nothing is hidden in the first window, it cannot be a popup
         delete this._initialState.windows[0].isPopup;
       }
       catch (ex) { debug("The session file is invalid: " + ex); }
     }
 
-    // remove the session data files if crash recovery is disabled
-    if (!this._resume_from_crash)
-      this._clearDisk();
-    else { // create a backup if the session data file exists
+    if (this._resume_from_crash) {
+      // create a backup if the session data file exists
       try {
         if (this._sessionFileBackup.exists())
           this._sessionFileBackup.remove(false);
         if (this._sessionFile.exists())
           this._sessionFile.copyTo(null, this._sessionFileBackup.leafName);
       }
       catch (ex) { Cu.reportError(ex); } // file was write-locked?
     }
@@ -313,22 +309,24 @@ SessionStoreService.prototype = {
     this.onLoad(aWindow);
   },
 
   /**
    * Called on application shutdown, after notifications:
    * quit-application-granted, quit-application
    */
   _uninit: function sss_uninit() {
-    if (this._doResumeSession()) { // save all data for session resuming 
-      this.saveState(true);
-    }
-    else { // discard all session related data 
+    // save all data for session resuming
+    this.saveState(true);
+
+    if (!this._doResumeSession()) {
+      // discard all session related data
       this._clearDisk();
     }
+
     // Make sure to break our cycle with the save timer
     if (this._saveTimer) {
       this._saveTimer.cancel();
       this._saveTimer = null;
     }
   },
 
   /**
@@ -354,25 +352,23 @@ SessionStoreService.prototype = {
         this._collectWindowData(aWindow);
       });
       this._dirtyWindows = [];
       break;
     case "quit-application-granted":
       // freeze the data at what we've got (ignoring closing windows)
       this._loadState = STATE_QUITTING;
       break;
-#ifndef XP_MACOSX
     case "browser-lastwindow-close-granted":
       // last browser window is quitting.
-      // remember to restore the last window when another browser window is openend
+      // remember to restore the last window when another browser window is opened
       // do not account for pref(resume_session_once) at this point, as it might be
       // set by another observer getting this notice after us
       this._restoreLastWindow = true;
       break;
-#endif
     case "quit-application":
       if (aData == "restart") {
         this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
         this._clearingOnShutdown = false;
       }
       this._loadState = STATE_QUITTING; // just to be sure
       this._uninit();
       break;
@@ -476,20 +472,19 @@ SessionStoreService.prototype = {
           this._saveTimer = null;
         }
         this.saveStateDelayed(null, -1);
         break;
       case "sessionstore.resume_from_crash":
         this._resume_from_crash = this._prefBranch.getBoolPref("sessionstore.resume_from_crash");
         // either create the file with crash recovery information or remove it
         // (when _loadState is not STATE_RUNNING, that file is used for session resuming instead)
-        if (this._resume_from_crash)
-          this.saveState(true);
-        else if (this._loadState == STATE_RUNNING)
+        if (!this._resume_from_crash)
           this._clearDisk();
+        this.saveState(true);
         break;
       }
       break;
     case "timer-callback": // timer call back for delayed saving
       this._saveTimer = null;
       this.saveState();
       break;
     case "private-browsing":
@@ -606,17 +601,18 @@ SessionStoreService.prototype = {
       this._loadState = STATE_RUNNING;
       this._lastSaveTime = Date.now();
       
       // restore a crashed session resp. resume the last session if requested
       if (this._initialState) {
         // make sure that the restored tabs are first in the window
         this._initialState._firstTabs = true;
         this._restoreCount = this._initialState.windows ? this._initialState.windows.length : 0;
-        this.restoreWindow(aWindow, this._initialState, this._isCmdLineEmpty(aWindow));
+        this.restoreWindow(aWindow, this._initialState,
+                           this._isCmdLineEmpty(aWindow, this._initialState));
         delete this._initialState;
         
         // _loadState changed from "stopped" to "running"
         // force a save operation so that crashes happening during startup are correctly counted
         this.saveState(true);
       }
       else {
         // Nothing to restore, notify observers things are complete.
@@ -626,47 +622,51 @@ SessionStoreService.prototype = {
         this._lastSaveTime -= this._interval;
       }
     }
     // this window was opened by _openWindowWithState
     else if (!this._isWindowLoaded(aWindow)) {
       let followUp = this._statesToRestore[aWindow.__SS_restoreID].windows.length == 1;
       this.restoreWindow(aWindow, this._statesToRestore[aWindow.__SS_restoreID], true, followUp);
     }
-#ifndef XP_MACOSX
     else if (this._restoreLastWindow && aWindow.toolbar.visible &&
-             this._closedWindows.length && this._doResumeSession() &&
+             this._closedWindows.length &&
              !this._inPrivateBrowsing) {
-
       // default to the most-recently closed window
       // don't use popup windows
       let state = null;
-      this._closedWindows = this._closedWindows.filter(function(aWinState) {
+      let newClosedWindows = this._closedWindows.filter(function(aWinState) {
         if (!state && !aWinState.isPopup) {
           state = aWinState;
           return false;
         }
         return true;
       });
       if (state) {
         delete state.hidden;
-        state = { windows: [state] };
-        this._restoreCount = 1;
-        this.restoreWindow(aWindow, state, this._isCmdLineEmpty(aWindow));
+#ifndef XP_MACOSX
+        if (!this._doResumeSession())
+#endif
+          state.tabs = state.tabs.filter(function (tab) tab.pinned);
+        if (state.tabs.length > 0) {
+          this._closedWindows = newClosedWindows;
+          this._restoreCount = 1;
+          state = { windows: [state] };
+          this.restoreWindow(aWindow, state, this._isCmdLineEmpty(aWindow, state));
+        }
       }
       // we actually restored the session just now.
       this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
     }
     if (this._restoreLastWindow && aWindow.toolbar.visible) {
       // always reset (if not a popup window)
       // we don't want to restore a window directly after, for example,
       // undoCloseWindow was executed.
       this._restoreLastWindow = false;
     }
-#endif
 
     var tabbrowser = aWindow.gBrowser;
     
     // add tab change listeners to all already existing tabs
     for (let i = 0; i < tabbrowser.browsers.length; i++) {
       this.onTabAdd(aWindow, tabbrowser.browsers[i], true);
     }
     // notification of tab add/remove/selection
@@ -1746,22 +1746,24 @@ SessionStoreService.prototype = {
     var sidebar = aWindow.document.getElementById("sidebar-box").getAttribute("sidebarcommand");
     if (sidebar)
       winData.sidebar = sidebar;
     else if (winData.sidebar)
       delete winData.sidebar;
   },
 
   /**
-   * serialize session data as Ini-formatted string
+   * gather session data as object
    * @param aUpdateAll
    *        Bool update all windows 
-   * @returns string
+   * @param aPinnedOnly
+   *        Bool collect pinned tabs only
+   * @returns object
    */
-  _getCurrentState: function sss_getCurrentState(aUpdateAll) {
+  _getCurrentState: function sss_getCurrentState(aUpdateAll, aPinnedOnly) {
     this._handleClosedWindows();
 
     var activeWindow = this._getMostRecentBrowserWindow();
     
     if (this._loadState == STATE_RUNNING) {
       // update the data for all windows with activities since the last save operation
       this._forEachBrowserWindow(function(aWindow) {
         if (!this._isWindowLoaded(aWindow)) // window data is still in _statesToRestore
@@ -1808,16 +1810,27 @@ SessionStoreService.prototype = {
       // prepend the last non-popup browser window, so that if the user loads more tabs
       // at startup we don't accidentally add them to a popup window
       do {
         total.unshift(lastClosedWindowsCopy.shift())
       } while (total[0].isPopup)
     }
 #endif
 
+    if (aPinnedOnly) {
+      total = total.filter(function (win) {
+        win.tabs = win.tabs.filter(function (tab) tab.pinned);
+        return win.tabs.length > 0;
+      });
+      if (total.length == 0)
+        return null;
+
+      lastClosedWindowsCopy = [];
+    }
+
     if (activeWindow) {
       this.activeWindowSSiCache = activeWindow.__SSi || "";
     }
     ix = windows.indexOf(this.activeWindowSSiCache);
     // We don't want to restore focus to a minimized window.
     if (ix != -1 && total[ix].sizemode == "minimized")
       ix = -1;
 
@@ -2556,18 +2569,17 @@ SessionStoreService.prototype = {
    * @param aDelay
    *        Milliseconds to delay
    */
   saveStateDelayed: function sss_saveStateDelayed(aWindow, aDelay) {
     if (aWindow) {
       this._dirtyWindows[aWindow.__SSi] = true;
     }
 
-    if (!this._saveTimer && this._resume_from_crash &&
-        !this._inPrivateBrowsing) {
+    if (!this._saveTimer && !this._inPrivateBrowsing) {
       // interval until the next disk operation is allowed
       var minimalDelay = this._lastSaveTime + this._interval - Date.now();
       
       // if we have to wait, set a timer, otherwise saveState directly
       aDelay = Math.max(minimalDelay, aDelay || 2000);
       if (aDelay > 0) {
         this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
         this._saveTimer.init(this, aDelay, Ci.nsITimer.TYPE_ONE_SHOT);
@@ -2579,25 +2591,33 @@ SessionStoreService.prototype = {
   },
 
   /**
    * save state to disk
    * @param aUpdateAll
    *        Bool update all windows 
    */
   saveState: function sss_saveState(aUpdateAll) {
-    // if crash recovery is disabled, only save session resuming information
-    if (!this._resume_from_crash && this._loadState == STATE_RUNNING)
-      return;
-
     // if we're in private browsing mode, do nothing
     if (this._inPrivateBrowsing)
       return;
 
-    var oState = this._getCurrentState(aUpdateAll);
+    var pinnedOnly = false;
+    if (this._loadState == STATE_QUITTING && !this._doResumeSession() ||
+        /* if crash recovery is disabled, only save session resuming information */
+        this._loadState == STATE_RUNNING && !this._resume_from_crash)
+      pinnedOnly = true;
+
+    var oState = this._getCurrentState(aUpdateAll, pinnedOnly);
+    if (!oState)
+      return;
+
+    if (pinnedOnly)
+      this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
+
     oState.session = {
       state: this._loadState == STATE_RUNNING ? STATE_RUNNING_STR : STATE_STOPPED_STR,
       lastUpdate: Date.now()
     };
     if (this._recentCrashes)
       oState.session.recentCrashes = this._recentCrashes;
 
     this._saveStateObject(oState);
@@ -2751,22 +2771,29 @@ SessionStoreService.prototype = {
   },
 
   /**
    * whether the user wants to load any other page at startup
    * (except the homepage) - needed for determining whether to overwrite the current tabs
    * C.f.: nsBrowserContentHandler's defaultArgs implementation.
    * @returns bool
    */
-  _isCmdLineEmpty: function sss_isCmdLineEmpty(aWindow) {
-    var defaultArgs = Cc["@mozilla.org/browser/clh;1"].
-                      getService(Ci.nsIBrowserHandler).defaultArgs;
-    if (aWindow.arguments && aWindow.arguments[0] &&
-        aWindow.arguments[0] == defaultArgs)
-      aWindow.arguments[0] = null;
+  _isCmdLineEmpty: function sss_isCmdLineEmpty(aWindow, aState) {
+    var pinnedOnly = aState.windows &&
+                     aState.windows.every(function (win)
+                       win.tabs.every(function (tab) tab.pinned));
+
+    if (!pinnedOnly) {
+      let defaultArgs = Cc["@mozilla.org/browser/clh;1"].
+                        getService(Ci.nsIBrowserHandler).defaultArgs;
+      if (aWindow.arguments &&
+          aWindow.arguments[0] &&
+          aWindow.arguments[0] == defaultArgs)
+        aWindow.arguments[0] = null;
+    }
 
     return !aWindow.arguments || !aWindow.arguments[0];
   },
 
   /**
    * don't save sensitive data if the user doesn't want to
    * (distinguishes between encrypted and non-encrypted sites)
    * @param aIsHTTPS
--- a/browser/components/sessionstore/test/browser/Makefile.in
+++ b/browser/components/sessionstore/test/browser/Makefile.in
@@ -94,29 +94,29 @@ include $(topsrcdir)/config/rules.mk
 	browser_465215.js \
 	browser_465223.js \
 	browser_466937.js \
 	browser_466937_sample.html \
 	browser_476161.js \
 	browser_476161_sample.html \
 	browser_477657.js \
 	browser_480148.js \
-	browser_480893.js \
 	browser_483330.js \
 	browser_485482.js \
 	browser_485482_sample.html \
 	browser_485563.js \
 	browser_490040.js \
 	browser_491168.js \
 	browser_491577.js \
 	browser_493467.js \
 	browser_495495.js \
 	browser_500328.js \
 	browser_514751.js \
 	browser_522545.js \
 	browser_524745.js \
 	browser_528776.js \
 	browser_579879.js \
+	browser_580512.js \
 	browser_586147.js \
 	$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/components/sessionstore/test/browser/browser_354894.js
+++ b/browser/components/sessionstore/test/browser/browser_354894.js
@@ -480,17 +480,17 @@ function test() {
        observing["browser-lastwindow-close-granted"] + 1,
        "Notification count for -request and -grant matches");
 
     executeSoon(nextFn);
   }
 
   /**
    * Test 8: Test if closing can be denied on Mac
-   *         Futhermore prepares the testNotificationCount test (Test 6)
+   *         Futhermore prepares the testNotificationCount test (Test 7)
    * @note: Mac only
    */
   function testMacNotifications(nextFn, iteration) {
     iteration = iteration || 1;
     setupTestAndRun(function(newWin) {
       // close the window
       // window.close doesn't push any close events,
       // so use BrowserTryToCloseWindow
deleted file mode 100644
--- a/browser/components/sessionstore/test/browser/browser_480893.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is sessionstore test code.
- *
- * The Initial Developer of the Original Code is
- * Michael Kohler <michaelkohler@live.com>.
- * Portions created by the Initial Developer are Copyright (C) 2009
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-function test() {
-  /** Test for Bug 480893 **/
-
-  waitForExplicitFinish();
-
-  // Test that starting a new session loads a blank page if Firefox is
-  // configured to display a blank page at startup (browser.startup.page = 0)
-  gPrefService.setIntPref("browser.startup.page", 0);
-  let tab = gBrowser.addTab("about:sessionrestore");
-  gBrowser.selectedTab = tab;
-  let browser = tab.linkedBrowser;
-  browser.addEventListener("load", function(aEvent) {
-    browser.removeEventListener("load", arguments.callee, true);
-    let doc = browser.contentDocument;
-
-    // click on the "Start New Session" button after about:sessionrestore is loaded
-    doc.getElementById("errorCancel").click();
-    browser.addEventListener("load", function(aEvent) {
-      browser.removeEventListener("load", arguments.callee, true);
-      let doc = browser.contentDocument;
-
-      is(doc.URL, "about:blank", "loaded page is about:blank");
-
-      // Test that starting a new session loads the homepage (set to http://mochi.test:8888)
-      // if Firefox is configured to display a homepage at startup (browser.startup.page = 1)
-      let homepage = "http://mochi.test:8888/";
-      gPrefService.setCharPref("browser.startup.homepage", homepage);
-      gPrefService.setIntPref("browser.startup.page", 1);
-      gBrowser.loadURI("about:sessionrestore");
-      browser.addEventListener("load", function(aEvent) {
-        browser.removeEventListener("load", arguments.callee, true);
-        let doc = browser.contentDocument;
-
-        // click on the "Start New Session" button after about:sessionrestore is loaded
-        doc.getElementById("errorCancel").click();
-        browser.addEventListener("load", function(aEvent) {
-          browser.removeEventListener("load", arguments.callee, true);
-          let doc = browser.contentDocument;
-
-          is(doc.URL, homepage, "loaded page is the homepage");
-
-          // close tab, restore default values and finish the test
-          gBrowser.removeTab(tab);
-          // we need this if-statement because if there is no user set value, 
-          // clearUserPref throws a uncatched exception and finish is not called
-          if (gPrefService.prefHasUserValue("browser.startup.page"))
-            gPrefService.clearUserPref("browser.startup.page");
-          gPrefService.clearUserPref("browser.startup.homepage");
-          finish();
-        }, true);
-      }, true);
-    }, true);
-  }, true);
-}
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser/browser_580512.js
@@ -0,0 +1,68 @@
+const URIS_PINNED = ["about:license", "about:about"];
+const URIS_NORMAL_A = ["about:mozilla"];
+const URIS_NORMAL_B = ["about:buildconfig"];
+
+function test() {
+  waitForExplicitFinish();
+
+  isnot(Services.prefs.getIntPref("browser.startup.page"), 3,
+        "pref to save session must not be set for this test");
+  ok(!Services.prefs.getBoolPref("browser.sessionstore.resume_session_once"),
+     "pref to save session once must not be set for this test");
+
+  document.documentElement.setAttribute("windowtype", "navigator:browsertestdummy");
+
+  openWinWithCb(closeFirstWin, URIS_PINNED.concat(URIS_NORMAL_A));
+}
+
+function closeFirstWin(win) {
+  win.gBrowser.pinTab(win.gBrowser.tabs[0]);
+  win.gBrowser.pinTab(win.gBrowser.tabs[1]);
+  win.BrowserTryToCloseWindow();
+  ok(win.closed, "window closed");
+
+  openWinWithCb(checkSecondWin, URIS_NORMAL_B, URIS_PINNED.concat(URIS_NORMAL_B));
+}
+
+function checkSecondWin(win) {
+  is(win.gBrowser.browsers[0].currentURI.spec, URIS_PINNED[0], "first pinned tab restored");
+  is(win.gBrowser.browsers[1].currentURI.spec, URIS_PINNED[1], "second pinned tab restored");
+  ok(win.gBrowser.tabs[0].pinned, "first pinned tab is still pinned");
+  ok(win.gBrowser.tabs[1].pinned, "second pinned tab is still pinned");
+  win.close();
+
+  // cleanup
+  document.documentElement.setAttribute("windowtype", "navigator:browser");
+  finish();
+}
+
+function openWinWithCb(cb, argURIs, expectedURIs) {
+  if (!expectedURIs)
+    expectedURIs = argURIs;
+
+  var win = openDialog("chrome://browser/content/", "_blank",
+                       "chrome,all,dialog=no", argURIs.join("|"));
+
+  win.addEventListener("load", function () {
+    info("the window loaded");
+
+    var expectedLoads = expectedURIs.length;
+
+    win.gBrowser.addTabsProgressListener({
+      onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+        if (aRequest &&
+            aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+            aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+            expectedURIs.indexOf(aRequest.QueryInterface(Ci.nsIChannel).originalURI.spec) > -1 &&
+            --expectedLoads <= 0) {
+          win.gBrowser.removeTabsProgressListener(this);
+          info("all tabs loaded");
+          is(win.gBrowser.tabs.length, expectedURIs.length, "didn't load any unexpected tabs");
+          executeSoon(function () {
+            cb(win);
+          });
+        }
+      }
+    });
+  }, false);
+}
--- a/browser/locales/en-US/chrome/browser-region/region.properties
+++ b/browser/locales/en-US/chrome/browser-region/region.properties
@@ -10,17 +10,17 @@ browser.search.order.2=Yahoo
 browser.contentHandlers.types.0.title=Bloglines
 browser.contentHandlers.types.0.uri=http://www.bloglines.com/login?r=/sub/%s
 browser.contentHandlers.types.1.title=My Yahoo!
 browser.contentHandlers.types.1.uri=http://add.my.yahoo.com/rss?url=%s
 browser.contentHandlers.types.2.title=Google
 browser.contentHandlers.types.2.uri=http://fusion.google.com/add?feedurl=%s
 
 # Keyword URL (for location bar searches)
-keyword.URL=http://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q=
+keyword.URL=http://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient&q=
 
 # URL for site-specific search engines
 # TRANSLATION NOTE: {moz:domain} and {searchTerms} are placeholders for the site
 # to be searched and the user's search query. Place them in the appropriate location
 # for your locale's URL but do not translate them.
 browser.search.siteSearchURL=http://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient&q=site%3A{moz:domain}+{searchTerms}
 
 # increment this number when anything gets changed in the list below.  This will
--- a/browser/locales/en-US/chrome/browser/aboutSessionRestore.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutSessionRestore.dtd
@@ -5,15 +5,13 @@
 <!ENTITY restorepage.errorTitle     "Well, this is embarrassing.">
 <!ENTITY restorepage.problemDesc    "&brandShortName; is having trouble recovering your windows and tabs. This is usually caused by a recently opened web page.">
 <!ENTITY restorepage.tryThis        "You can try:">
 <!ENTITY restorepage.restoreSome    "Removing one or more tabs that you think may be causing the problem">
 <!ENTITY restorepage.startNew       "Starting an entirely new browsing session">
 
 <!ENTITY restorepage.tryagainButton "Restore">
 <!ENTITY restorepage.restore.access "R">
-<!ENTITY restorepage.cancelButton   "Start New Session">
-<!ENTITY restorepage.cancel.access  "S">
 
 <!ENTITY restorepage.restoreHeader  "Restore">
 <!ENTITY restorepage.listHeader     "Windows and Tabs">
 <!-- LOCALIZATION NOTE: &#37;S will be replaced with a number. -->
 <!ENTITY restorepage.windowLabel    "Window &#37;S">
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -127,18 +127,18 @@
 <!ENTITY backForwardItem.title        "Back/Forward">
 <!ENTITY locationItem.title           "Location">
 <!ENTITY searchItem.title             "Search">
 <!ENTITY throbberItem.title           "Activity Indicator">
 <!ENTITY bookmarksItem.title          "Bookmarks">
 
 <!-- Toolbar items --> 
 <!ENTITY  homeButton.label            "Home">
-<!ENTITY tabViewButton.label          "Tab Sets">
-<!ENTITY tabViewButton.tooltip        "Open a visual tab interface">
+<!ENTITY tabViewButton2.label         "Tab Groups">
+<!ENTITY tabViewButton2.tooltip       "Group Your Tabs">
 
 <!ENTITY bookmarksButton.label          "Bookmarks">
 <!ENTITY bookmarksButton.tooltip        "Display your bookmarks">
 <!ENTITY bookmarksButton.accesskey  "B">
 <!ENTITY bookmarksCmd.commandkey "b">
 
 <!ENTITY bookmarksMenuButton.label          "Bookmarks">
 <!ENTITY bookmarksMenuButton.tooltip        "Display your bookmarks">
@@ -235,18 +235,18 @@
 <!ENTITY privateBrowsingCmd.start.label         "Start Private Browsing">
 <!ENTITY privateBrowsingCmd.start.accesskey     "P">
 <!ENTITY privateBrowsingCmd.stop.label          "Stop Private Browsing">
 <!ENTITY privateBrowsingCmd.stop.accesskey      "P">
 <!ENTITY privateBrowsingCmd.commandkey          "P">
 
 <!ENTITY viewMenu.label         "View"> 
 <!ENTITY viewMenu.accesskey       "V"> 
-<!ENTITY showTabView.label     "TabView">
-<!ENTITY showTabView.accesskey     "V">
+<!ENTITY showTabView2.label     "Group Your Tabs…">
+<!ENTITY showTabView2.accesskey     "G">
 <!ENTITY viewToolbarsMenu.label       "Toolbars"> 
 <!ENTITY viewToolbarsMenu.accesskey     "T"> 
 <!ENTITY viewSidebarMenu.label "Sidebar">
 <!ENTITY viewSidebarMenu.accesskey "e">
 <!ENTITY viewCustomizeToolbar.label       "Customize…"> 
 <!ENTITY viewCustomizeToolbar.accesskey     "C">
 <!ENTITY viewTabsOnTop.label            "Tabs on Top">
 <!ENTITY viewTabsOnTop.accesskey        "T">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -271,12 +271,12 @@ privateBrowsingNeverAsk=&Do not show thi
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 ctrlTab.showAll.label=;Show all #1 tabs
 
 # LOCALIZATION NOTE (addKeywordTitleAutoFill): %S will be replaced by the page's title
 # Used as the bookmark name when saving a keyword for a search field.
 addKeywordTitleAutoFill=Search %S
 
 # TabView
-tabView.title=%S Tab Sets
+tabView2.title=%S - Group Your Tabs
 
 extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.name=Default
 extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.description=The default theme.
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -280,17 +280,17 @@ toolbarbutton.bookmark-item > menupopup 
 }
 
 #bookmarksToolbarFolderMenu,
 #BMB_bookmarksToolbarFolderMenu {
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png");
 }
 
 #BMB_unsortedBookmarksFolderMenu {
-  list-style-image: url("chrome://browser/skin/places/unsortedBookmarks.png");
+  list-style-image: url("chrome://browser/skin/places/unfiledBookmarks.png");
 }
 
 /* ----- PRIMARY TOOLBAR BUTTONS ----- */	
 
 .toolbarbutton-1,
 #restore-button,
 toolbarbutton[type="menu-button"] > .toolbarbutton-menubutton-button,
 toolbarbutton[type="menu-button"] > .toolbarbutton-menubutton-dropmarker {
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -105,16 +105,17 @@ MOZ_JSDEBUGGER  = @MOZ_JSDEBUGGER@
 MOZ_IPC 	= @MOZ_IPC@
 MOZ_IPDL_TESTS 	= @MOZ_IPDL_TESTS@
 MOZ_LEAKY	= @MOZ_LEAKY@
 MOZ_MEMORY      = @MOZ_MEMORY@
 MOZ_JPROF       = @MOZ_JPROF@
 MOZ_SHARK       = @MOZ_SHARK@
 MOZ_CALLGRIND   = @MOZ_CALLGRIND@
 MOZ_VTUNE       = @MOZ_VTUNE@
+MOZ_TRACE_JSCALLS = @MOZ_TRACE_JSCALLS@
 MOZ_TRACEVIS    = @MOZ_TRACEVIS@
 DEHYDRA_PATH    = @DEHYDRA_PATH@
 
 NS_TRACE_MALLOC = @NS_TRACE_MALLOC@
 USE_ELF_DYNSTR_GC = @USE_ELF_DYNSTR_GC@
 INCREMENTAL_LINKER = @INCREMENTAL_LINKER@
 MACOSX_DEPLOYMENT_TARGET = @MACOSX_DEPLOYMENT_TARGET@
 MOZ_MAIL_NEWS	= @MOZ_MAIL_NEWS@
--- a/configure.in
+++ b/configure.in
@@ -6165,17 +6165,17 @@ fi
 dnl ========================================================
 dnl = Breakpad crash reporting (on by default on supported platforms)
 dnl ========================================================
 
 case $target in
 i?86-*-mingw*)
   MOZ_CRASHREPORTER=1
   ;;
-i?86-apple-darwin*|powerpc-apple-darwin*)
+i?86-apple-darwin*|powerpc-apple-darwin*|x86_64-apple-darwin*)
   MOZ_CRASHREPORTER=1
   ;;
 i?86-*-linux*|x86_64-*-linux*|arm-*-linux*)
   MOZ_CRASHREPORTER=1
   ;;
 *solaris*)
   MOZ_CRASHREPORTER=1
   ;;
@@ -7360,16 +7360,27 @@ fi
 dnl ========================================================
 dnl = Location of malloc wrapper lib
 dnl ========================================================
 MOZ_ARG_WITH_STRING(wrap-malloc,
 [  --with-wrap-malloc=DIR  Location of malloc wrapper library],
     WRAP_MALLOC_LIB=$withval)
 
 dnl ========================================================
+dnl = Use JS Call tracing
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(trace-jscalls,
+[  --enable-trace-jscalls  Enable JS call enter/exit callback (default=no)],
+    MOZ_TRACE_JSCALLS=1,
+    MOZ_TRACE_JSCALLS= )
+if test -n "$MOZ_TRACE_JSCALLS"; then
+    AC_DEFINE(MOZ_TRACE_JSCALLS)
+fi
+
+dnl ========================================================
 dnl = Use TraceVis
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(tracevis,
 [  --enable-tracevis       Enable TraceVis tracing tool (default=no)],
     MOZ_TRACEVIS=1,
     MOZ_TRACEVIS= )
 if test -n "$MOZ_TRACEVIS"; then
     AC_DEFINE(MOZ_TRACEVIS)
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -113,18 +113,18 @@ class Loader;
 namespace dom {
 class Link;
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 
 #define NS_IDOCUMENT_IID      \
-{ 0xda512fdc, 0x2d83, 0x44b0, \
-  { 0xb0, 0x99, 0x00, 0xc6, 0xbb, 0x72, 0x39, 0xed } }
+{ 0xb2274bc3, 0x4a1c, 0x4e64, \
+  { 0x8d, 0xe4, 0x3b, 0xc6, 0x50, 0x28, 0x84, 0x38 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Document states
 
 // RTL locale: specific to the XUL localedir attribute
 #define NS_DOCUMENT_STATE_RTL_LOCALE              (1 << 0)
@@ -308,27 +308,32 @@ public:
    * to remove it.
    */
   typedef PRBool (* IDTargetObserver)(Element* aOldElement,
                                       Element* aNewelement, void* aData);
 
   /**
    * Add an IDTargetObserver for a specific ID. The IDTargetObserver
    * will be fired whenever the content associated with the ID changes
-   * in the future. At most one (aObserver, aData) pair can be registered
-   * for each ID.
+   * in the future. If aForImage is true, mozSetImageElement can override
+   * what content is associated with the ID. In that case the IDTargetObserver
+   * will be notified at those times when the result of LookupImageElement
+   * changes.
+   * At most one (aObserver, aData, aForImage) triple can be
+   * registered for each ID.
    * @return the content currently associated with the ID.
    */
   virtual Element* AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
-                                       void* aData) = 0;
+                                       void* aData, PRBool aForImage) = 0;
   /**
-   * Remove the (aObserver, aData) pair for a specific ID, if registered.
+   * Remove the (aObserver, aData, aForImage) triple for a specific ID, if
+   * registered.
    */
-  virtual void RemoveIDTargetObserver(nsIAtom* aID,
-                                      IDTargetObserver aObserver, void* aData) = 0;
+  virtual void RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
+                                      void* aData, PRBool aForImage) = 0;
 
   /**
    * Get the Content-Type of this document.
    * (This will always return NS_OK, but has this signature to be compatible
    *  with nsIDOMNSDocument::GetContentType())
    */
   NS_IMETHOD GetContentType(nsAString& aContentType) = 0;
 
@@ -670,23 +675,20 @@ public:
   /**
    * Get the script loader for this document
    */ 
   virtual nsScriptLoader* ScriptLoader() = 0;
 
   /**
    * Add/Remove an element to the document's id and name hashes
    */
-  virtual void AddToIdTable(mozilla::dom::Element* aElement, nsIAtom* aId) = 0;
-  virtual void RemoveFromIdTable(mozilla::dom::Element* aElement,
-                                 nsIAtom* aId) = 0;
-  virtual void AddToNameTable(mozilla::dom::Element* aElement,
-                              nsIAtom* aName) = 0;
-  virtual void RemoveFromNameTable(mozilla::dom::Element* aElement,
-                                   nsIAtom* aName) = 0;
+  virtual void AddToIdTable(Element* aElement, nsIAtom* aId) = 0;
+  virtual void RemoveFromIdTable(Element* aElement, nsIAtom* aId) = 0;
+  virtual void AddToNameTable(Element* aElement, nsIAtom* aName) = 0;
+  virtual void RemoveFromNameTable(Element* aElement, nsIAtom* aName) = 0;
 
   //----------------------------------------------------------------------
 
   // Document notification API's
 
   /**
    * Add a new observer of document change notifications. Whenever
    * content is changed, appended, inserted or removed the observers are
@@ -1401,17 +1403,26 @@ public:
   virtual void SetChangeScrollPosWhenScrollingToRef(PRBool aValue) = 0;
 
   /**
    * This method is similar to GetElementById() from nsIDOMDocument but it
    * returns a mozilla::dom::Element instead of a nsIDOMElement.
    * It prevents converting nsIDOMElement to mozill:dom::Element which is
    * already converted from mozilla::dom::Element.
    */
-  virtual mozilla::dom::Element* GetElementById(const nsAString& aElementId) = 0;
+  virtual Element* GetElementById(const nsAString& aElementId) = 0;
+
+  /**
+   * Lookup an image element using its associated ID, which is usually provided
+   * by |-moz-element()|. Similar to GetElementById, with the difference that
+   * elements set using mozSetImageElement have higher priority.
+   * @param aId the ID associated the element we want to lookup
+   * @return the element associated with |aId|
+   */
+  virtual Element* LookupImageElement(const nsAString& aElementId) = 0;
 
   void ScheduleBeforePaintEvent();
   void BeforePaintEventFiring()
   {
     mHavePendingPaint = PR_FALSE;
   }
 
 protected:
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -315,17 +315,18 @@ public:
 
 #ifdef MOZILLA_INTERNAL_API
   nsINode(already_AddRefed<nsINodeInfo> aNodeInfo)
   : mNodeInfo(aNodeInfo),
     mParentPtrBits(0),
     mFlagsOrSlots(NODE_DOESNT_HAVE_SLOTS),
     mNextSibling(nsnull),
     mPreviousSibling(nsnull),
-    mFirstChild(nsnull)
+    mFirstChild(nsnull),
+    mNodeHasRenderingObservers(false)
   {
   }
 
 #endif
 
   virtual ~nsINode();
 
   /**
@@ -1142,16 +1143,20 @@ public:
       if (parent == aRoot) {
         return nsnull;
       }
       cur = parent;
     }
     NS_NOTREACHED("How did we get here?");
   }
 
+  bool HasRenderingObservers() { return mNodeHasRenderingObservers; }
+  void SetHasRenderingObservers(bool aValue)
+    { mNodeHasRenderingObservers = aValue; }
+
   // Optimized way to get classinfo. May return null.
   virtual nsXPCClassInfo* GetClassInfo() = 0;
 protected:
 
   // Override this function to create a custom slots class.
   virtual nsINode::nsSlots* CreateSlots();
 
   PRBool HasSlots() const
@@ -1274,16 +1279,19 @@ protected:
    * NODE_* macros for the layout of the bits in this
    * member.
    */
   PtrBits mFlagsOrSlots;
 
   nsIContent* mNextSibling;
   nsIContent* mPreviousSibling;
   nsIContent* mFirstChild;
+
+  // More flags
+  bool mNodeHasRenderingObservers : 1;
 };
 
 
 extern const nsIID kThisPtrOffsetsSID;
 
 // _implClass is the class to use to cast to nsISupports
 #define NS_OFFSET_AND_INTERFACE_TABLE_BEGIN_AMBIGUOUS(_class, _implClass)     \
   static const QITableEntry offsetAndQITable[] = {                            \
--- a/content/base/public/nsReferencedElement.h
+++ b/content/base/public/nsReferencedElement.h
@@ -83,18 +83,21 @@ public:
    * Set up the reference. This can be called multiple times to
    * change which reference is being tracked, but these changes
    * do not trigger ElementChanged.
    * @param aFrom the source element for context
    * @param aURI the URI containing a hash-reference to the element
    * @param aWatch if false, then we do not set up the notifications to track
    * changes, so ElementChanged won't fire and get() will always return the same
    * value, the current element for the ID.
+   * @param aReferenceImage whether the ID references image elements which are
+   * subject to the document's mozSetImageElement overriding mechanism.
    */
-  void Reset(nsIContent* aFrom, nsIURI* aURI, PRBool aWatch = PR_TRUE);
+  void Reset(nsIContent* aFrom, nsIURI* aURI, PRBool aWatch = PR_TRUE,
+             PRBool aReferenceImage = PR_FALSE);
 
   /**
    * A variation on Reset() to set up a reference that consists of the ID of
    * an element in the same document as aFrom.
    * @param aFrom the source element for context
    * @param aID the ID of the element
    * @param aWatch if false, then we do not set up the notifications to track
    * changes, so ElementChanged won't fire and get() will always return the same
@@ -203,11 +206,12 @@ private:
     nsString mRef;
   };
   friend class DocumentLoadNotification;
   
   nsCOMPtr<nsIAtom>      mWatchID;
   nsCOMPtr<nsIDocument>  mWatchDocument;
   nsRefPtr<Element> mElement;
   nsRefPtr<Notification> mPendingNotification;
+  PRPackedBool           mReferencingImage;
 };
 
 #endif /*NSREFERENCEDELEMENT_H_*/
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -233,102 +233,130 @@ nsIdentifierMapEntry::Traverse(nsCycleCo
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "mIdentifierMap mDocAllList");
   aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mDocAllList));
 
   for (PRInt32 i = 0; i < mIdContentList.Count(); ++i) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
                                        "mIdentifierMap mIdContentList element");
     aCallback->NoteXPCOMChild(static_cast<nsIContent*>(mIdContentList[i]));
   }
+
+  if (mImageElement) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
+                                       "mIdentifierMap mImageElement element");
+    nsIContent* imageElement = mImageElement;
+    aCallback->NoteXPCOMChild(imageElement);
+  }
 }
 
 void
 nsIdentifierMapEntry::SetInvalidName()
 {
   mNameContentList = NAME_NOT_VALID;
 }
 
 PRBool
 nsIdentifierMapEntry::IsInvalidName()
 {
   return mNameContentList == NAME_NOT_VALID;
 }
 
+PRBool
+nsIdentifierMapEntry::IsEmpty()
+{
+  return mIdContentList.Count() == 0 && !mNameContentList &&
+         !mChangeCallbacks && !mImageElement;
+}
+
 nsresult
 nsIdentifierMapEntry::CreateNameContentList()
 {
   mNameContentList = new nsBaseContentList();
   NS_ENSURE_TRUE(mNameContentList, NS_ERROR_OUT_OF_MEMORY);
   NS_ADDREF(mNameContentList);
   return NS_OK;
 }
 
 Element*
 nsIdentifierMapEntry::GetIdElement()
 {
   return static_cast<Element*>(mIdContentList.SafeElementAt(0));
 }
 
+Element*
+nsIdentifierMapEntry::GetImageIdElement()
+{
+  return mImageElement ? mImageElement.get() : GetIdElement();
+}
+
 void
 nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<nsIContent>* aElements)
 {
   for (PRInt32 i = 0; i < mIdContentList.Count(); ++i) {
     aElements->AppendObject(static_cast<Element*>(mIdContentList[i]));
   }
 }
 
 void
 nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
-                                               void* aData)
+                                               void* aData, PRBool aForImage)
 {
   if (!mChangeCallbacks) {
     mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>;
     if (!mChangeCallbacks)
       return;
     mChangeCallbacks->Init();
   }
 
-  ChangeCallback cc = { aCallback, aData };
+  ChangeCallback cc = { aCallback, aData, aForImage };
   mChangeCallbacks->PutEntry(cc);
 }
 
 void
 nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
-                                                  void* aData)
+                                                  void* aData, PRBool aForImage)
 {
   if (!mChangeCallbacks)
     return;
-  ChangeCallback cc = { aCallback, aData };
+  ChangeCallback cc = { aCallback, aData, aForImage };
   mChangeCallbacks->RemoveEntry(cc);
   if (mChangeCallbacks->Count() == 0) {
     mChangeCallbacks = nsnull;
   }
 }
 
 struct FireChangeArgs {
   Element* mFrom;
   Element* mTo;
+  PRBool mImageOnly;
+  PRBool mHaveImageOverride;
 };
 
 static PLDHashOperator
 FireChangeEnumerator(nsIdentifierMapEntry::ChangeCallbackEntry *aEntry, void *aArg)
 {
   FireChangeArgs* args = static_cast<FireChangeArgs*>(aArg);
+  // Don't fire image changes for non-image observers, and don't fire element
+  // changes for image observers when an image override is active.
+  if (aEntry->mKey.mForImage ? (args->mHaveImageOverride && !args->mImageOnly) :
+                               args->mImageOnly)
+    return PL_DHASH_NEXT;
   return aEntry->mKey.mCallback(args->mFrom, args->mTo, aEntry->mKey.mData)
       ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
 }
 
 void
 nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement,
-                                          Element* aNewElement)
+                                          Element* aNewElement,
+                                          PRBool aImageOnly)
 {
   if (!mChangeCallbacks)
     return;
 
-  FireChangeArgs args = { aOldElement, aNewElement };
+  FireChangeArgs args = { aOldElement, aNewElement, aImageOnly, !!mImageElement };
   mChangeCallbacks->EnumerateEntries(FireChangeEnumerator, &args);
 }
 
 PRBool
 nsIdentifierMapEntry::AddIdElement(Element* aElement)
 {
   NS_PRECONDITION(aElement, "Must have element");
   NS_PRECONDITION(mIdContentList.IndexOf(nsnull) < 0,
@@ -381,36 +409,46 @@ nsIdentifierMapEntry::AddIdElement(Eleme
     Element* oldElement =
       static_cast<Element*>(mIdContentList.SafeElementAt(1));
     NS_ASSERTION(currentElement == oldElement, "How did that happen?");
     FireChangeCallbacks(oldElement, aElement);
   }
   return PR_TRUE;
 }
 
-PRBool
+void
 nsIdentifierMapEntry::RemoveIdElement(Element* aElement)
 {
   // This should only be called while the document is in an update.
   // Assertions near the call to this method guarantee this.
 
   // XXXbz should this ever Compact() I guess when all the content is gone
   // we'll just get cleaned up in the natural order of things...
   Element* currentElement =
     static_cast<Element*>(mIdContentList.SafeElementAt(0));
   if (!mIdContentList.RemoveElement(aElement))
-    return PR_FALSE;
+    return;
   if (currentElement == aElement) {
     FireChangeCallbacks(currentElement,
                         static_cast<Element*>(mIdContentList.SafeElementAt(0)));
   }
   // Make sure the release happens after the check above, since it'll
   // null out aContent.
   NS_RELEASE(aElement);
-  return mIdContentList.Count() == 0 && !mNameContentList && !mChangeCallbacks;
+}
+
+void
+nsIdentifierMapEntry::SetImageElement(Element* aElement)
+{
+  Element* oldElement = GetImageIdElement();
+  mImageElement = aElement;
+  Element* newElement = GetImageIdElement();
+  if (oldElement != newElement) {
+    FireChangeCallbacks(oldElement, newElement, PR_TRUE);
+  }
 }
 
 void
 nsIdentifierMapEntry::AddNameElement(Element* aElement)
 {
   if (!mNameContentList || mNameContentList == NAME_NOT_VALID)
     return;
 
@@ -2477,17 +2515,18 @@ nsDocument::RemoveFromIdTable(Element *a
     return;
   }
 
   nsIdentifierMapEntry *entry =
     mIdentifierMap.GetEntry(nsDependentAtomString(aId));
   if (!entry) // Can be null for XML elements with changing ids.
     return;
 
-  if (entry->RemoveIdElement(aElement)) {
+  entry->RemoveIdElement(aElement);
+  if (entry->IsEmpty()) {
     mIdentifierMap.RawRemoveEntry(entry);
   }
 }
 
 nsIPrincipal*
 nsDocument::GetPrincipal()
 {
   return NodePrincipal();
@@ -3870,45 +3909,73 @@ nsDocument::GetElementById(const nsAStri
 
   *aReturn = nsnull;
 
   return NS_OK;
 }
 
 Element*
 nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
-                                void* aData)
+                                void* aData, PRBool aForImage)
 {
   nsDependentAtomString id(aID);
 
   if (!CheckGetElementByIdArg(id))
     return nsnull;
 
   nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id);
   NS_ENSURE_TRUE(entry, nsnull);
 
-  entry->AddContentChangeCallback(aObserver, aData);
-  return entry->GetIdElement();
+  entry->AddContentChangeCallback(aObserver, aData, aForImage);
+  return aForImage ? entry->GetImageIdElement() : entry->GetIdElement();
 }
 
 void
-nsDocument::RemoveIDTargetObserver(nsIAtom* aID,
-                                   IDTargetObserver aObserver, void* aData)
+nsDocument::RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
+                                   void* aData, PRBool aForImage)
 {
   nsDependentAtomString id(aID);
 
   if (!CheckGetElementByIdArg(id))
     return;
 
   nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id);
   if (!entry) {
     return;
   }
 
-  entry->RemoveContentChangeCallback(aObserver, aData);
+  entry->RemoveContentChangeCallback(aObserver, aData, aForImage);
+}
+
+NS_IMETHODIMP
+nsDocument::MozSetImageElement(const nsAString& aImageElementId,
+                               nsIDOMElement* aImageElement)
+{
+  if (aImageElementId.IsEmpty())
+    return NS_OK;
+
+  nsCOMPtr<nsIContent> content = do_QueryInterface(aImageElement);
+  nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aImageElementId);
+  if (entry) {
+    entry->SetImageElement(content ? content->AsElement() : nsnull);
+    if (entry->IsEmpty()) {
+      mIdentifierMap.RemoveEntry(aImageElementId);
+    }
+  }
+  return NS_OK;
+}
+
+Element*
+nsDocument::LookupImageElement(const nsAString& aId)
+{
+  if (aId.IsEmpty())
+    return nsnull;
+
+  nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aId);
+  return entry ? entry->GetImageIdElement() : nsnull;
 }
 
 void
 nsDocument::DispatchContentLoadedEvents()
 {
   NS_TIME_FUNCTION;
   // If you add early returns from this method, make sure you're
   // calling UnblockOnload properly.
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -167,93 +167,109 @@ public:
 
   void SetInvalidName();
   PRBool IsInvalidName();
   void AddNameElement(Element* aElement);
   void RemoveNameElement(Element* aElement);
   PRBool HasNameContentList() {
     return mNameContentList != nsnull;
   }
+  PRBool IsEmpty();
   nsBaseContentList* GetNameContentList() {
     return mNameContentList;
   }
   nsresult CreateNameContentList();
 
   /**
    * Returns the element if we know the element associated with this
    * id. Otherwise returns null.
    */
   Element* GetIdElement();
   /**
+   * If this entry has a non-null image element set (using SetImageElement),
+   * the image element will be returned, otherwise the same as GetIdElement().
+   */
+  Element* GetImageIdElement();
+  /**
    * Append all the elements with this id to aElements
    */
   void AppendAllIdContent(nsCOMArray<nsIContent>* aElements);
   /**
    * This can fire ID change callbacks.
    * @return true if the content could be added, false if we failed due
    * to OOM.
    */
   PRBool AddIdElement(Element* aElement);
   /**
    * This can fire ID change callbacks.
-   * @return true if this map entry should be removed
    */
-  PRBool RemoveIdElement(Element* aElement);
+  void RemoveIdElement(Element* aElement);
+  /**
+   * Set the image element override for this ID. This will be returned by
+   * GetIdElement(PR_TRUE) if non-null.
+   */
+  void SetImageElement(Element* aElement);
 
   PRBool HasContentChangeCallback() { return mChangeCallbacks != nsnull; }
-  void AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback, void* aData);
-  void RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback, void* aData);
+  void AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
+                                void* aData, PRBool aForImage);
+  void RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
+                                void* aData, PRBool aForImage);
 
   void Traverse(nsCycleCollectionTraversalCallback* aCallback);
 
   void SetDocAllList(nsContentList* aContentList) { mDocAllList = aContentList; }
   nsContentList* GetDocAllList() { return mDocAllList; }
 
   struct ChangeCallback {
     nsIDocument::IDTargetObserver mCallback;
     void* mData;
+    PRBool mForImage;
   };
 
   struct ChangeCallbackEntry : public PLDHashEntryHdr {
     typedef const ChangeCallback KeyType;
     typedef const ChangeCallback* KeyTypePointer;
 
     ChangeCallbackEntry(const ChangeCallback* key) :
       mKey(*key) { }
     ChangeCallbackEntry(const ChangeCallbackEntry& toCopy) :
       mKey(toCopy.mKey) { }
 
     KeyType GetKey() const { return mKey; }
     PRBool KeyEquals(KeyTypePointer aKey) const {
       return aKey->mCallback == mKey.mCallback &&
-             aKey->mData == mKey.mData;
+             aKey->mData == mKey.mData &&
+             aKey->mForImage == mKey.mForImage;
     }
 
     static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; }
     static PLDHashNumber HashKey(KeyTypePointer aKey)
     {
       return (NS_PTR_TO_INT32(aKey->mCallback) >> 2) ^
              (NS_PTR_TO_INT32(aKey->mData));
     }
     enum { ALLOW_MEMMOVE = PR_TRUE };
     
     ChangeCallback mKey;
   };
 
 private:
-  void FireChangeCallbacks(Element* aOldElement, Element* aNewElement);
+  void FireChangeCallbacks(Element* aOldElement, Element* aNewElement,
+                           PRBool aImageOnly = PR_FALSE);
 
   // empty if there are no elementswith this ID.
   // The elementsnodes are stored addrefed.
   nsSmallVoidArray mIdContentList;
   // NAME_NOT_VALID if this id cannot be used as a 'name'.  Otherwise
   // stores Elements.
   nsBaseContentList *mNameContentList;
   nsRefPtr<nsContentList> mDocAllList;
   nsAutoPtr<nsTHashtable<ChangeCallbackEntry> > mChangeCallbacks;
+  nsCOMPtr<Element> mImageElement;
 };
 
 class nsDocHeaderData
 {
 public:
   nsDocHeaderData(nsIAtom* aField, const nsAString& aData)
     : mField(aField), mData(aData), mNext(nsnull)
   {
@@ -568,19 +584,19 @@ public:
   virtual nsresult AddCharSetObserver(nsIObserver* aObserver);
 
   /**
    * Remove a charset observer.
    */
   virtual void RemoveCharSetObserver(nsIObserver* aObserver);
 
   virtual Element* AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
-                                       void* aData);
-  virtual void RemoveIDTargetObserver(nsIAtom* aID,
-                                      IDTargetObserver aObserver, void* aData);
+                                       void* aData, PRBool aForImage);
+  virtual void RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
+                                      void* aData, PRBool aForImage);
 
   /**
    * Access HTTP header data (this may also get set from other sources, like
    * HTML META tags).
    */
   virtual void GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const;
   virtual void SetHeaderData(nsIAtom* aheaderField,
                              const nsAString& aData);
@@ -661,23 +677,20 @@ public:
   /**
    * Get the script loader for this document
    */
   virtual nsScriptLoader* ScriptLoader();
 
   /**
    * Add/Remove an element to the document's id and name hashes
    */
-  virtual void AddToIdTable(mozilla::dom::Element* aElement, nsIAtom* aId);
-  virtual void RemoveFromIdTable(mozilla::dom::Element* aElement,
-                                 nsIAtom* aId);
-  virtual void AddToNameTable(mozilla::dom::Element* aElement,
-                              nsIAtom* aName);
-  virtual void RemoveFromNameTable(mozilla::dom::Element* aElement,
-                                   nsIAtom* aName);
+  virtual void AddToIdTable(Element* aElement, nsIAtom* aId);
+  virtual void RemoveFromIdTable(Element* aElement, nsIAtom* aId);
+  virtual void AddToNameTable(Element* aElement, nsIAtom* aName);
+  virtual void RemoveFromNameTable(Element* aElement, nsIAtom* aName);
 
   /**
    * Add a new observer of document change notifications. Whenever
    * content is changed, appended, inserted or removed the observers are
    * informed.
    */
   virtual void AddObserver(nsIDocumentObserver* aObserver);
 
@@ -954,17 +967,19 @@ public:
   virtual void SetChangeScrollPosWhenScrollingToRef(PRBool aValue);
 
   already_AddRefed<nsContentList>
     GetElementsByTagName(const nsAString& aTagName);
   already_AddRefed<nsContentList>
     GetElementsByTagNameNS(const nsAString& aNamespaceURI,
                            const nsAString& aLocalName);
 
-  virtual mozilla::dom::Element *GetElementById(const nsAString& aElementId);
+  virtual Element *GetElementById(const nsAString& aElementId);
+
+  virtual Element *LookupImageElement(const nsAString& aElementId);
 
 protected:
   friend class nsNodeUtils;
 
   /**
    * Check that aId is not empty and log a message to the console
    * service if it is.
    * @returns PR_TRUE if aId looks correct, PR_FALSE otherwise.
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -789,16 +789,17 @@ GK_ATOM(radiogroup, "radiogroup")
 GK_ATOM(readonly, "readonly")
 GK_ATOM(rect, "rect")
 GK_ATOM(rectangle, "rectangle")
 GK_ATOM(ref, "ref")
 GK_ATOM(refresh, "refresh")
 GK_ATOM(rel, "rel")
 GK_ATOM(rem, "rem")
 GK_ATOM(removeelement, "removeelement")
+GK_ATOM(renderingobserverlist, "renderingobserverlist")
 GK_ATOM(repeat, "repeat")
 GK_ATOM(replace, "replace")
 GK_ATOM(reset, "reset")
 GK_ATOM(resizeafter, "resizeafter")
 GK_ATOM(resizebefore, "resizebefore")
 GK_ATOM(resizer, "resizer")
 GK_ATOM(resolution, "resolution")
 GK_ATOM(resource, "resource")
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -71,16 +71,19 @@
 #include "nsIFrame.h"
 #include "nsIDOMNode.h"
 
 #include "nsContentUtils.h"
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsDOMClassInfo.h"
+#ifdef MOZ_SVG
+#include "nsSVGEffects.h"
+#endif
 
 #include "mozAutoDocUpdate.h"
 
 #ifdef DEBUG_chb
 static void PrintReqURL(imgIRequest* req) {
   if (!req) {
     printf("(null req)\n");
     return;
@@ -148,17 +151,17 @@ nsImageLoadingContent::~nsImageLoadingCo
   PR_END_MACRO
 
 
 /*
  * imgIContainerObserver impl
  */
 NS_IMETHODIMP
 nsImageLoadingContent::FrameChanged(imgIContainer* aContainer,
-                                    nsIntRect* aDirtyRect)
+                                    const nsIntRect* aDirtyRect)
 {
   LOOP_OVER_OBSERVERS(FrameChanged(aContainer, aDirtyRect));
   return NS_OK;
 }
             
 /*
  * imgIDecoderObserver impl
  */
@@ -323,16 +326,21 @@ nsImageLoadingContent::OnStopDecode(imgI
 
   // Fire the appropriate DOM event.
   if (NS_SUCCEEDED(aStatus)) {
     FireEvent(NS_LITERAL_STRING("load"));
   } else {
     FireEvent(NS_LITERAL_STRING("error"));
   }
 
+#ifdef MOZ_SVG
+  nsCOMPtr<nsINode> thisNode = do_QueryInterface(this);
+  nsSVGEffects::InvalidateDirectRenderingObservers(thisNode->AsElement());
+#endif
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest, PRBool aLastPart)
 {
   LOOP_OVER_OBSERVERS(OnStopRequest(aRequest, aLastPart));
 
--- a/content/base/src/nsReferencedElement.cpp
+++ b/content/base/src/nsReferencedElement.cpp
@@ -70,17 +70,18 @@ static PRBool EqualExceptRef(nsIURL* aUR
   url2->SetRef(EmptyCString());
 
   PRBool equal;
   rv = url1->Equals(url2, &equal);
   return NS_SUCCEEDED(rv) && equal;
 }
 
 void
-nsReferencedElement::Reset(nsIContent* aFromContent, nsIURI* aURI, PRBool aWatch)
+nsReferencedElement::Reset(nsIContent* aFromContent, nsIURI* aURI,
+                           PRBool aWatch, PRBool aReferenceImage)
 {
   Unlink();
 
   nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
   if (!url)
     return;
 
   nsCAutoString refPart;
@@ -170,16 +171,18 @@ nsReferencedElement::Reset(nsIContent* a
 
   if (aWatch) {
     nsCOMPtr<nsIAtom> atom = do_GetAtom(ref);
     if (!atom)
       return;
     atom.swap(mWatchID);
   }
 
+  mReferencingImage = aReferenceImage;
+
   HaveNewDocument(doc, aWatch, ref);
 }
 
 void
 nsReferencedElement::ResetWithID(nsIContent* aFromContent, const nsString& aID,
                                  PRBool aWatch)
 {
   nsIDocument *doc = aFromContent->GetCurrentDoc();
@@ -190,36 +193,40 @@ nsReferencedElement::ResetWithID(nsICont
 
   if (aWatch) {
     nsCOMPtr<nsIAtom> atom = do_GetAtom(aID);
     if (!atom)
       return;
     atom.swap(mWatchID);
   }
 
+  mReferencingImage = PR_FALSE;
+
   HaveNewDocument(doc, aWatch, aID);
 }
 
 void
 nsReferencedElement::HaveNewDocument(nsIDocument* aDocument, PRBool aWatch,
                                      const nsString& aRef)
 {
   if (aWatch) {
     mWatchDocument = aDocument;
     if (mWatchDocument) {
-      mElement = mWatchDocument->AddIDTargetObserver(mWatchID, Observe, this);
+      mElement = mWatchDocument->AddIDTargetObserver(mWatchID, Observe, this,
+                                                     mReferencingImage);
     }
     return;
   }
   
   if (!aDocument) {
     return;
   }
 
-  Element *e = aDocument->GetElementById(aRef);
+  Element *e = mReferencingImage ? aDocument->LookupImageElement(aRef) :
+                                   aDocument->GetElementById(aRef);
   if (e) {
     mElement = e;
   }
 }
 
 void
 nsReferencedElement::Traverse(nsCycleCollectionTraversalCallback* aCB)
 {
@@ -228,25 +235,27 @@ nsReferencedElement::Traverse(nsCycleCol
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCB, "mContent");
   aCB->NoteXPCOMChild(mElement);
 }
 
 void
 nsReferencedElement::Unlink()
 {
   if (mWatchDocument && mWatchID) {
-    mWatchDocument->RemoveIDTargetObserver(mWatchID, Observe, this);
+    mWatchDocument->RemoveIDTargetObserver(mWatchID, Observe, this,
+                                           mReferencingImage);
   }
   if (mPendingNotification) {
     mPendingNotification->Clear();
     mPendingNotification = nsnull;
   }
   mWatchDocument = nsnull;
   mWatchID = nsnull;
   mElement = nsnull;
+  mReferencingImage = PR_FALSE;
 }
 
 PRBool
 nsReferencedElement::Observe(Element* aOldElement,
                              Element* aNewElement, void* aData)
 {
   nsReferencedElement* p = static_cast<nsReferencedElement*>(aData);
   if (p->mPendingNotification) {
--- a/content/base/src/nsStubImageDecoderObserver.cpp
+++ b/content/base/src/nsStubImageDecoderObserver.cpp
@@ -103,12 +103,12 @@ nsStubImageDecoderObserver::OnStopReques
 NS_IMETHODIMP 
 nsStubImageDecoderObserver::OnDiscard(imgIRequest *aRequest)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStubImageDecoderObserver::FrameChanged(imgIContainer *aContainer,
-                                         nsIntRect * aDirtyRect)
+                                         const nsIntRect *aDirtyRect)
 {
     return NS_OK;
 }
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -51,16 +51,20 @@
 #include "gfxPattern.h"
 #include "gfxUtils.h"
 
 #include "CanvasUtils.h"
 #include "NativeJSContext.h"
 
 #include "GLContextProvider.h"
 
+#ifdef MOZ_SVG
+#include "nsSVGEffects.h"
+#endif
+
 #include "prenv.h"
 
 using namespace mozilla;
 using namespace mozilla::gl;
 
 nsresult NS_NewCanvasRenderingContextWebGL(nsICanvasRenderingContextWebGL** aResult);
 
 nsresult
@@ -204,16 +208,20 @@ WebGLContext::DestroyResourcesAndContext
 }
 
 void
 WebGLContext::Invalidate()
 {
     if (!mCanvasElement)
         return;
 
+#ifdef MOZ_SVG
+    nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
+#endif
+
     if (mInvalidated)
         return;
 
     mInvalidated = PR_TRUE;
     HTMLCanvasElement()->InvalidateFrame();
 }
 
 /* readonly attribute nsIDOMHTMLCanvasElement canvas; */
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -141,16 +141,20 @@
 
 using namespace mozilla::ipc;
 #endif
 
 #ifdef MOZ_X11
 #include "gfxXlibSurface.h"
 #endif
 
+#ifdef MOZ_SVG
+#include "nsSVGEffects.h"
+#endif
+
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 
 #ifndef M_PI
 #define M_PI		3.14159265358979323846
 #define M_PI_2		1.57079632679489661923
 #endif
@@ -1034,16 +1038,20 @@ nsCanvasRenderingContext2D::ApplyStyle(S
 nsresult
 nsCanvasRenderingContext2D::Redraw()
 {
     if (!mCanvasElement) {
         NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
         return NS_OK;
     }
 
+#ifdef MOZ_SVG
+    nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
+#endif
+
     if (mIsEntireFrameInvalid)
         return NS_OK;
 
     mIsEntireFrameInvalid = PR_TRUE;
 
     HTMLCanvasElement()->InvalidateFrame();
 
     return NS_OK;
@@ -1052,16 +1060,20 @@ nsCanvasRenderingContext2D::Redraw()
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Redraw(const gfxRect& r)
 {
     if (!mCanvasElement) {
         NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
         return NS_OK;
     }
 
+#ifdef MOZ_SVG
+    nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
+#endif
+
     if (mIsEntireFrameInvalid)
         return NS_OK;
 
     if (++mInvalidateCount > kCanvasMaxInvalidateCount)
         return Redraw();
 
     HTMLCanvasElement()->InvalidateFrame(&r);
 
--- a/content/canvas/test/Makefile.in
+++ b/content/canvas/test/Makefile.in
@@ -62,16 +62,20 @@ include $(topsrcdir)/config/rules.mk
 	image_rgrg-256x256.png \
 	image_red.png \
 	image_transparent.png \
 	image_green.png \
 	image_green-redirect \
 	image_green-redirect^headers^ \
 	test_drawImageIncomplete.html \
 	test_canvas_font_setter.html \
+	test_2d.composite.uncovered.image.source-in.html \
+	test_2d.composite.uncovered.image.destination-in.html \
+	test_2d.composite.uncovered.image.source-out.html \
+	test_2d.composite.uncovered.image.destination-atop.html \
 	$(NULL)
 
 # xor and lighter aren't well handled by cairo; they mostly work, but we don't want
 # to test that
 #	test_2d.composite.solid.xor.html \
 #	test_2d.composite.solid.lighter.html \
 #	test_2d.composite.transparent.xor.html \
 #	test_2d.composite.transparent.lighter.html \
@@ -108,23 +112,16 @@ ifneq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 _TEST_FILES_0 += \
 	test_2d.gradient.radial.inside2.html \
 	test_2d.gradient.radial.inside3.html \
 	test_2d.gradient.radial.outside1.html \
 	test_2d.gradient.radial.cone.front.html \
 	test_2d.gradient.radial.cone.top.html \
 	$(NULL)
 
-_TEST_FILES_0 += \
-	test_2d.composite.uncovered.image.source-in.html \
-	test_2d.composite.uncovered.image.destination-in.html \
-	test_2d.composite.uncovered.image.source-out.html \
-	test_2d.composite.uncovered.image.destination-atop.html \
-	$(NULL)
-
 # This is another Quartz bug -- closed paths that don't lie fully within the
 # destination bounds seem to have problems with the BEVEL/SQUARE join/cap combo.
 # The joins are rendered as if with MITER; the correct behaviour is also seen
 # if BUTT is used instead of SQUARE.
 _TEST_FILES_0 += test_2d.line.cap.closed.html
 
 endif
 
--- a/content/media/nsMediaDecoder.cpp
+++ b/content/media/nsMediaDecoder.cpp
@@ -49,16 +49,19 @@
 #include "nsHTMLMediaElement.h"
 #include "nsAutoLock.h"
 #include "nsIRenderingContext.h"
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
 #include "nsPresContext.h"
 #include "nsDOMError.h"
 #include "nsDisplayList.h"
+#ifdef MOZ_SVG
+#include "nsSVGEffects.h"
+#endif
 
 #if defined(XP_MACOSX)
 #include "gfxQuartzImageSurface.h"
 #endif
 
 // Number of milliseconds between progress events as defined by spec
 #define PROGRESS_MS 350
 
@@ -151,16 +154,20 @@ void nsMediaDecoder::Invalidate()
     }
   }
 
   if (frame) {
     nsRect contentRect = frame->GetContentRect() - frame->GetPosition();
     // Only the layer needs to be updated here
     frame->InvalidateLayer(contentRect, nsDisplayItem::TYPE_VIDEO);
   }
+
+#ifdef MOZ_SVG
+  nsSVGEffects::InvalidateDirectRenderingObservers(mElement);
+#endif
 }
 
 static void ProgressCallback(nsITimer* aTimer, void* aClosure)
 {
   nsMediaDecoder* decoder = static_cast<nsMediaDecoder*>(aClosure);
   decoder->Progress(PR_TRUE);
 }
 
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -5332,17 +5332,18 @@ public:
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
   virtual PRInt32 IntrinsicState() const;
 
   // imgIDecoderObserver
   NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult status,
                           const PRUnichar *statusArg);
   // imgIContainerObserver
-  NS_IMETHOD FrameChanged(imgIContainer *aContainer, nsIntRect *dirtyRect);
+  NS_IMETHOD FrameChanged(imgIContainer *aContainer,
+                          const nsIntRect *aDirtyRect);
   // imgIContainerObserver
   NS_IMETHOD OnStartContainer(imgIRequest *aRequest,
                               imgIContainer *aContainer);
 
   void MaybeLoadSVGImage();
 
   virtual nsXPCClassInfo* GetClassInfo();
 private:
@@ -5601,20 +5602,20 @@ nsSVGFEImageElement::OnStopDecode(imgIRe
   nsresult rv =
     nsImageLoadingContent::OnStopDecode(aRequest, status, statusArg);
   Invalidate();
   return rv;
 }
 
 NS_IMETHODIMP
 nsSVGFEImageElement::FrameChanged(imgIContainer *aContainer,
-                                  nsIntRect *dirtyRect)
+                                  const nsIntRect *aDirtyRect)
 {
   nsresult rv =
-    nsImageLoadingContent::FrameChanged(aContainer, dirtyRect);
+    nsImageLoadingContent::FrameChanged(aContainer, aDirtyRect);
   Invalidate();
   return rv;
 }
 
 NS_IMETHODIMP
 nsSVGFEImageElement::OnStartContainer(imgIRequest *aRequest,
                                       imgIContainer *aContainer)
 {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -8093,17 +8093,21 @@ nsDocShell::InternalLoad(nsIURI * aURI,
                 NS_ENSURE_TRUE(hEntry, NS_ERROR_FAILURE);
                 nsCOMPtr<nsISHEntry> shEntry(do_QueryInterface(hEntry));
                 if (shEntry)
                     shEntry->SetTitle(mTitle);
             }
 
             /* Set the title for the Global History entry for this anchor url.
              */
-            if (mGlobalHistory) {
+            nsCOMPtr<IHistory> history = services::GetHistoryService();
+            if (history) {
+                history->SetURITitle(aURI, mTitle);
+            }
+            else if (mGlobalHistory) {
                 mGlobalHistory->SetPageTitle(aURI, mTitle);
             }
 
             if (sameDocIdent) {
                 // Set the doc's URI according to the new history entry's URI
                 nsCOMPtr<nsIURI> newURI;
                 mOSHE->GetURI(getter_AddRefs(newURI));
                 NS_ENSURE_TRUE(newURI, NS_ERROR_FAILURE);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -490,28 +490,34 @@ using namespace mozilla::dom;
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 static const char kDOMStringBundleURL[] =
   "chrome://global/locale/dom/dom.properties";
 
 // NOTE: DEFAULT_SCRIPTABLE_FLAGS and DOM_DEFAULT_SCRIPTABLE_FLAGS
 //       are defined in nsIDOMClassInfo.h.
 
-#define WINDOW_SCRIPTABLE_FLAGS                                               \
+#define COMMON_WINDOW_SCRIPTABLE_FLAGS                                        \
  (nsIXPCScriptable::WANT_GETPROPERTY |                                        \
   nsIXPCScriptable::WANT_SETPROPERTY |                                        \
   nsIXPCScriptable::WANT_PRECREATE |                                          \
   nsIXPCScriptable::WANT_ADDPROPERTY |                                        \
   nsIXPCScriptable::WANT_DELPROPERTY |                                        \
-  nsIXPCScriptable::WANT_NEWENUMERATE |                                       \
   nsIXPCScriptable::WANT_FINALIZE |                                           \
   nsIXPCScriptable::WANT_EQUALITY |                                           \
-  nsIXPCScriptable::WANT_OUTER_OBJECT |                                       \
+  nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE)
+
+#define INNER_WINDOW_SCRIPTABLE_FLAGS                                         \
+ (COMMON_WINDOW_SCRIPTABLE_FLAGS |                                            \
+  nsIXPCScriptable::WANT_OUTER_OBJECT)                                        \
+
+#define OUTER_WINDOW_SCRIPTABLE_FLAGS                                         \
+ (COMMON_WINDOW_SCRIPTABLE_FLAGS |                                            \
   nsIXPCScriptable::WANT_INNER_OBJECT |                                       \
-  nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE)
+  nsIXPCScriptable::WANT_NEWENUMERATE)
 
 #define NODE_SCRIPTABLE_FLAGS                                                 \
  ((DOM_DEFAULT_SCRIPTABLE_FLAGS |                                             \
    nsIXPCScriptable::WANT_GETPROPERTY |                                       \
    nsIXPCScriptable::WANT_ADDPROPERTY |                                       \
    nsIXPCScriptable::WANT_SETPROPERTY) &                                      \
   ~nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY)
 
@@ -628,21 +634,21 @@ static nsDOMClassInfoData sClassInfoData
   // flattened set (i.e. nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY
   // is not set), because of this make sure all scriptable interfaces
   // that are implemented by nsGlobalWindow can securely be exposed
   // to JS.
 
 
   NS_DEFINE_CLASSINFO_DATA(Window, nsOuterWindowSH,
                            DEFAULT_SCRIPTABLE_FLAGS |
-                           WINDOW_SCRIPTABLE_FLAGS)
+                           OUTER_WINDOW_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(InnerWindow, nsInnerWindowSH,
                            DEFAULT_SCRIPTABLE_FLAGS |
-                           WINDOW_SCRIPTABLE_FLAGS)
+                           INNER_WINDOW_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(Location, nsLocationSH,
                            (DOM_DEFAULT_SCRIPTABLE_FLAGS &
                             ~nsIXPCScriptable::ALLOW_PROP_MODS_TO_PROTOTYPE))
 
   NS_DEFINE_CLASSINFO_DATA(Navigator, nsNavigatorSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS |
                            nsIXPCScriptable::WANT_PRECREATE)
@@ -923,21 +929,21 @@ static nsDOMClassInfoData sClassInfoData
   // more putting things in those categories up there.  New entries
   // are to be added to the end of the list
   NS_DEFINE_CLASSINFO_DATA(CSSRect, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   // DOM Chrome Window class.
   NS_DEFINE_CLASSINFO_DATA(ChromeWindow, nsOuterWindowSH,
                            DEFAULT_SCRIPTABLE_FLAGS |
-                           WINDOW_SCRIPTABLE_FLAGS)
+                           OUTER_WINDOW_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(InnerChromeWindow, nsInnerWindowSH,
                            DEFAULT_SCRIPTABLE_FLAGS |
-                           WINDOW_SCRIPTABLE_FLAGS)
+                           INNER_WINDOW_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(CSSRGBColor, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(RangeException, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(CSSValueList, nsCSSValueListSH,
@@ -1310,21 +1316,21 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(FileError, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(FileReader, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(ModalContentWindow, nsOuterWindowSH,
                            DEFAULT_SCRIPTABLE_FLAGS |
-                           WINDOW_SCRIPTABLE_FLAGS)
+                           OUTER_WINDOW_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(InnerModalContentWindow, nsInnerWindowSH,
                            DEFAULT_SCRIPTABLE_FLAGS |
-                           WINDOW_SCRIPTABLE_FLAGS)
+                           INNER_WINDOW_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DataContainerEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(MessageEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(GeoGeolocation, nsDOMGenericSH,
@@ -5374,44 +5380,25 @@ nsCommonWindowSH::SetProperty(nsIXPConne
 
     return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING;
   }
 
   return nsEventReceiverSH::SetProperty(wrapper, cx, obj, id, vp, _retval);
 }
 
 NS_IMETHODIMP
-nsCommonWindowSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                              JSObject *obj, jsid id, jsval *vp,
-                              PRBool *_retval)
+nsOuterWindowSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                             JSObject *obj, jsid id, jsval *vp,
+                             PRBool *_retval)
 {
   nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper);
 
-#ifdef DEBUG_SH_FORWARDING
-  {
-    nsDependentJSString str(::JS_ValueToString(cx, id));
-
-    if (win->IsInnerWindow()) {
-#ifdef DEBUG_PRINT_INNER
-      printf("Property '%s' add on inner window %p\n",
-             NS_ConvertUTF16toUTF8(str).get(), (void *)win);
-#endif
-    } else {
-      printf("Property '%s' add on outer window %p\n",
-             NS_ConvertUTF16toUTF8(str).get(), (void *)win);
-    }
-  }
-#endif
-
   JSObject *realObj;
   wrapper->GetJSObject(&realObj);
-  if (win->IsOuterWindow() && obj == realObj) {
-    // XXXjst: Do security checks here when we remove the security
-    // checks on the inner window.
-
+  if (obj == realObj) {
     nsGlobalWindow *innerWin = win->GetCurrentInnerWindowInternal();
 
     JSObject *innerObj;
     if (innerWin && (innerObj = innerWin->GetGlobalJSObject())) {
       if (sResolving) {
         return NS_OK;
       }
 
@@ -6518,48 +6505,32 @@ nsCommonWindowSH::GlobalResolve(nsGlobal
 static JSBool
 ContentWindowGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                     jsval *rval)
 {
   return ::JS_GetProperty(cx, obj, "content", rval);
 }
 
 PRBool
-nsCommonWindowSH::sResolving = PR_FALSE;
-
-NS_IMETHODIMP
-nsCommonWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                             JSObject *obj, jsid id, PRUint32 flags,
-                             JSObject **objp, PRBool *_retval)
+nsOuterWindowSH::sResolving = PR_FALSE;
+
+NS_IMETHODIMP
+nsOuterWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                            JSObject *obj, jsid id, PRUint32 flags,
+                            JSObject **objp, PRBool *_retval)
 {
   nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper);
 
-#ifdef DEBUG_SH_FORWARDING
-  {
-    nsDependentJSString str(::JS_ValueToString(cx, id));
-
-    if (win->IsInnerWindow()) {
-#ifdef DEBUG_PRINT_INNER
-      printf("Property '%s' resolve on inner window %p\n",
-             NS_ConvertUTF16toUTF8(str).get(), (void *)win);
-#endif
-    } else {
-      printf("Property '%s' resolve on outer window %p\n",
-             NS_ConvertUTF16toUTF8(str).get(), (void *)win);
-    }
-  }
-#endif
-
   // Note, we won't forward resolve of the location property to the
   // inner window, we need to deal with that one for the outer too
   // since we've got special security protection code for that
   // property.  Also note that we want to enter this block even for
   // native wrappers, so that we'll ensure an inner window to wrap
   // against for the result of whatever we're getting.
-  if (win->IsOuterWindow() && id != sLocation_id) {
+  if (id != sLocation_id) {
     // XXXjst: Do security checks here when we remove the security
     // checks on the inner window.
 
     nsGlobalWindow *innerWin = win->GetCurrentInnerWindowInternal();
 
     if ((!innerWin || !innerWin->GetExtantDocument()) &&
         !win->IsCreatingInnerWindow()) {
       // We're resolving a property on an outer window for which there
@@ -6630,16 +6601,27 @@ nsCommonWindowSH::NewResolve(nsIXPConnec
 
         *objp = desc.obj;
       }
 
       return NS_OK;
     }
   }
 
+  return nsCommonWindowSH::NewResolve(wrapper, cx, obj, id, flags, objp,
+                                      _retval);
+}
+
+NS_IMETHODIMP
+nsCommonWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                             JSObject *obj, jsid id, PRUint32 flags,
+                             JSObject **objp, PRBool *_retval)
+{
+  nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper);
+
   if (!JSID_IS_STRING(id)) {
     if (JSID_IS_INT(id) && !(flags & JSRESOLVE_ASSIGNING)) {
       // If we're resolving a numeric property, treat that as if
       // window.frames[n] is resolved (since window.frames ===
       // window), if window.frames[n] is a child frame, define a
       // property for this index.
 
       nsCOMPtr<nsIDOMWindow> frame = GetChildFrame(win, id);
@@ -7154,42 +7136,40 @@ nsCommonWindowSH::NewResolve(nsIXPConnec
       *objp = obj;
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsCommonWindowSH::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                               JSObject *obj, PRUint32 enum_op, jsval *statep,
-                               jsid *idp, PRBool *_retval)
+nsOuterWindowSH::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                              JSObject *obj, PRUint32 enum_op, jsval *statep,
+                              jsid *idp, PRBool *_retval)
 {
   switch ((JSIterateOp)enum_op) {
     /* FIXME bug 576449: non-enumerable property support */
     case JSENUMERATE_INIT_ALL:
     case JSENUMERATE_INIT:
     {
+#ifdef DEBUG
       // First, do the security check that nsDOMClassInfo does to see
       // if we need to do any work at all.
       nsDOMClassInfo::Enumerate(wrapper, cx, obj, _retval);
       if (!*_retval) {
+        NS_ERROR("security wrappers failed us");
         return NS_OK;
       }
+#endif
 
       // The security check passed, let's see if we need to get the inner
       // window's JS object or if we can just start enumerating.
-      nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper);
-      JSObject *enumobj = win->GetGlobalJSObject();
-      if (win->IsOuterWindow()) {
-        nsGlobalWindow *inner = win->GetCurrentInnerWindowInternal();
-        if (inner) {
-          enumobj = inner->GetGlobalJSObject();
-        }
-      }
+      nsGlobalWindow *win =
+        nsGlobalWindow::FromWrapper(wrapper)->GetCurrentInnerWindowInternal();
+      JSObject *enumobj = win->FastGetGlobalJSObject();
 
       // Great, we have the js object, now let's enumerate it.
       JSObject *iterator = JS_NewPropertyIterator(cx, enumobj);
       if (!iterator) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
 
       *statep = OBJECT_TO_JSVAL(iterator);
@@ -7267,18 +7247,18 @@ nsCommonWindowSH::Equality(nsIXPConnectW
 
     *bp = win->GetOuterWindow() == other->GetOuterWindow();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsCommonWindowSH::OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
-                              JSObject * obj, JSObject * *_retval)
+nsInnerWindowSH::OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                             JSObject * obj, JSObject * *_retval)
 {
   nsGlobalWindow *origWin = nsGlobalWindow::FromWrapper(wrapper);
   nsGlobalWindow *win = origWin->GetOuterWindowInternal();
 
   if (!win) {
     // If we no longer have an outer window. No code should ever be
     // running on a window w/o an outer, which means this hook should
     // never be called when we have no outer. But just in case, return
@@ -7295,34 +7275,34 @@ nsCommonWindowSH::OuterObject(nsIXPConne
     return NS_OK;
   }
 
   *_retval = winObj;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsCommonWindowSH::InnerObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
-                              JSObject * obj, JSObject * *_retval)
+nsOuterWindowSH::InnerObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                             JSObject * obj, JSObject * *_retval)
 {
   nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper);
 
-  if (win->IsInnerWindow() || win->IsFrozen()) {
+  if (win->IsFrozen()) {
     // Return the inner window, or the outer if we're dealing with a
     // frozen outer.
 
     *_retval = obj;
   } else {
     // Try to find the current inner window.
 
     nsGlobalWindow *inner = win->GetCurrentInnerWindowInternal();
     if (!inner) {
       // Yikes! No inner window! Instead of leaking the outer window into the
       // scope chain, let's return an error.
-
+      NS_ERROR("using an outer that doesn't have an inner?");
       *_retval = nsnull;
 
       return NS_ERROR_UNEXPECTED;
     }
 
     *_retval = inner->FastGetGlobalJSObject();
   }
 
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -508,18 +508,16 @@ protected:
   virtual ~nsCommonWindowSH()
   {
   }
 
   static nsresult GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
                                 JSObject *obj, JSString *str,
                                 PRBool *did_resolve);
 
-  static PRBool sResolving;
-
 public:
   NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
                        JSObject *globalObj, JSObject **parentObj);
 #ifdef DEBUG
   NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj)
   {
     nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryWrappedNative(wrapper));
@@ -539,34 +537,25 @@ public:
 
     return rv;
   }
 #endif
   NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsid id, jsval *vp, PRBool *_retval);
   NS_IMETHOD SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsid id, jsval *vp, PRBool *_retval);
-  NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                         JSObject *obj, jsid id, jsval *vp, PRBool *_retval);
   NS_IMETHOD DelProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsid id, jsval *vp, PRBool *_retval);
   NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj, jsid id, PRUint32 flags,
                         JSObject **objp, PRBool *_retval);
-  NS_IMETHOD NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                          JSObject *obj, PRUint32 enum_op, jsval *statep,
-                          jsid *idp, PRBool *_retval);
   NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                       JSObject *obj);
   NS_IMETHOD Equality(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
                       JSObject * obj, const jsval &val, PRBool *bp);
-  NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
-                         JSObject * obj, JSObject * *_retval);
-  NS_IMETHOD InnerObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
-                         JSObject * obj, JSObject * *_retval);
 
   static JSBool GlobalScopePolluterNewResolve(JSContext *cx, JSObject *obj,
                                               jsid id, uintN flags,
                                               JSObject **objp);
   static JSBool GlobalScopePolluterGetProperty(JSContext *cx, JSObject *obj,
                                                jsid id, jsval *vp);
   static JSBool SecurityCheckOnSetProp(JSContext *cx, JSObject *obj, jsid id,
                                        jsval *vp);
@@ -581,17 +570,30 @@ protected:
   nsOuterWindowSH(nsDOMClassInfoData* aData) : nsCommonWindowSH(aData)
   {
   }
 
   virtual ~nsOuterWindowSH()
   {
   }
 
+  static PRBool sResolving;
+
 public:
+  NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                         JSObject *obj, jsid id, jsval *vp, PRBool *_retval);
+  NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                        JSObject *obj, jsid id, PRUint32 flags,
+                        JSObject **objp, PRBool *_retval);
+  NS_IMETHOD NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                          JSObject *obj, PRUint32 enum_op, jsval *statep,
+                          jsid *idp, PRBool *_retval);
+  NS_IMETHOD InnerObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                         JSObject * obj, JSObject * *_retval);
+
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsOuterWindowSH(aData);
   }
 };
 
 class nsInnerWindowSH : public nsCommonWindowSH
 {
@@ -600,16 +602,20 @@ protected:
   {
   }
 
   virtual ~nsInnerWindowSH()
   {
   }
 
 public:
+  // We WANT_ADDPROPERTY, but are content to inherit it from nsEventReceiverSH.
+  NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+                         JSObject * obj, JSObject * *_retval);
+
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsInnerWindowSH(aData);
   }
 };
 
 
 // Location scriptable helper
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -921,17 +921,17 @@ nsGlobalWindow::CleanUp(PRBool aIgnoreMo
       return;
     }
   }
 
   // Guarantee idempotence.
   if (mCleanedUp)
     return;
   mCleanedUp = PR_TRUE;
-    
+
   mNavigator = nsnull;
   mScreen = nsnull;
   mHistory = nsnull;
   mMenubar = nsnull;
   mToolbar = nsnull;
   mLocationbar = nsnull;
   mPersonalbar = nsnull;
   mStatusbar = nsnull;
@@ -1072,16 +1072,18 @@ nsGlobalWindow::FreeInnerObjects(PRBool 
 
   mChromeEventHandler = nsnull;
 
   if (mListenerManager) {
     mListenerManager->Disconnect();
     mListenerManager = nsnull;
   }
 
+  mLocation = nsnull;
+
   if (mDocument) {
     NS_ASSERTION(mDoc, "Why is mDoc null?");
 
     // Remember the document's principal.
     mDocumentPrincipal = mDoc->NodePrincipal();
   }
 
 #ifdef DEBUG
@@ -1505,59 +1507,53 @@ class WindowStateHolder : public nsISupp
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID)
   NS_DECL_ISUPPORTS
 
   WindowStateHolder(nsGlobalWindow *aWindow,
                     nsIXPConnectJSObjectHolder *aHolder,
                     nsNavigator *aNavigator,
-                    nsLocation *aLocation,
                     nsIXPConnectJSObjectHolder *aOuterProto);
 
   nsGlobalWindow* GetInnerWindow() { return mInnerWindow; }
   nsIXPConnectJSObjectHolder *GetInnerWindowHolder()
   { return mInnerWindowHolder; }
 
   nsNavigator* GetNavigator() { return mNavigator; }
-  nsLocation* GetLocation() { return mLocation; }
   nsIXPConnectJSObjectHolder* GetOuterProto() { return mOuterProto; }
 
   void DidRestoreWindow()
   {
     mInnerWindow = nsnull;
 
     mInnerWindowHolder = nsnull;
     mNavigator = nsnull;
-    mLocation = nsnull;
     mOuterProto = nsnull;
   }
 
 protected:
   ~WindowStateHolder();
 
   nsGlobalWindow *mInnerWindow;
   // We hold onto this to make sure the inner window doesn't go away. The outer
   // window ends up recalculating it anyway.
   nsCOMPtr<nsIXPConnectJSObjectHolder> mInnerWindowHolder;
   nsRefPtr<nsNavigator> mNavigator;
-  nsRefPtr<nsLocation> mLocation;
   nsCOMPtr<nsIXPConnectJSObjectHolder> mOuterProto;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID)
 
 WindowStateHolder::WindowStateHolder(nsGlobalWindow *aWindow,
                                      nsIXPConnectJSObjectHolder *aHolder,
                                      nsNavigator *aNavigator,
-                                     nsLocation *aLocation,
                                      nsIXPConnectJSObjectHolder *aOuterProto)
   : mInnerWindow(aWindow),
     mNavigator(aNavigator),
-    mLocation(aLocation),
     mOuterProto(aOuterProto)
 {
   NS_PRECONDITION(aWindow, "null window");
   NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window");
 
   mInnerWindowHolder = aHolder;
 
   aWindow->SuspendTimeouts();
@@ -1749,17 +1745,16 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
       NS_ASSERTION(wsh, "What kind of weird state are you giving me here?");
 
       newInnerWindow = wsh->GetInnerWindow();
       mInnerWindowHolder = wsh->GetInnerWindowHolder();
 
       // These assignments addref.
       mNavigator = wsh->GetNavigator();
-      mLocation = wsh->GetLocation();
 
       if (mNavigator) {
         // Update mNavigator's docshell pointer now.
         mNavigator->SetDocShell(mDocShell);
         mNavigator->LoadingNewDocument();
       }
     } else {
       if (thisChrome) {
@@ -1768,18 +1763,16 @@ nsGlobalWindow::SetNewDocument(nsIDocume
         isChrome = PR_TRUE;
       } else {
         if (mIsModalContentWindow) {
           newInnerWindow = new nsGlobalModalWindow(this);
         } else {
           newInnerWindow = new nsGlobalWindow(this);
         }
       }
-
-      mLocation = nsnull;
     }
 
     if (!newInnerWindow) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     if (currentInner && currentInner->mJSObject) {
       if (mNavigator && !aState) {
@@ -2186,18 +2179,16 @@ nsGlobalWindow::SetDocShell(nsIDocShell*
     nsCycleCollector_DEBUG_shouldBeFreed(static_cast<nsIScriptGlobalObject*>(this));
 #endif
   }
 
   mDocShell = aDocShell;        // Weak Reference
 
   if (mNavigator)
     mNavigator->SetDocShell(aDocShell);
-  if (mLocation)
-    mLocation->SetDocShell(aDocShell);
   if (mHistory)
     mHistory->SetDocShell(aDocShell);
   if (mFrames)
     mFrames->SetDocShell(aDocShell);
   if (mScreen)
     mScreen->SetDocShell(aDocShell);
 
   if (!mDocShell) {
@@ -6838,22 +6829,23 @@ nsGlobalWindow::GetPrivateRoot()
   return static_cast<nsGlobalWindow *>
                     (static_cast<nsIDOMWindow *>(top));
 }
 
 
 NS_IMETHODIMP
 nsGlobalWindow::GetLocation(nsIDOMLocation ** aLocation)
 {
-  FORWARD_TO_OUTER(GetLocation, (aLocation), NS_ERROR_NOT_INITIALIZED);
+  FORWARD_TO_INNER(GetLocation, (aLocation), NS_ERROR_NOT_INITIALIZED);
 
   *aLocation = nsnull;
 
-  if (!mLocation && mDocShell) {
-    mLocation = new nsLocation(mDocShell);
+  nsIDocShell *docShell = GetDocShell();
+  if (!mLocation && docShell) {
+    mLocation = new nsLocation(docShell);
     if (!mLocation) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   NS_IF_ADDREF(*aLocation = mLocation);
 
   return NS_OK;
@@ -9120,17 +9112,16 @@ nsGlobalWindow::SaveWindowState(nsISuppo
   nsresult rv = nsContentUtils::XPConnect()->
     GetWrappedNativePrototype((JSContext *)mContext->GetNativeContext(),
                               mJSObject, ci, getter_AddRefs(proto));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsISupports> state = new WindowStateHolder(inner,
                                                       mInnerWindowHolder,
                                                       mNavigator,
-                                                      mLocation,
                                                       proto);
   NS_ENSURE_TRUE(state, NS_ERROR_OUT_OF_MEMORY);
 
 #ifdef DEBUG_PAGE_CACHE
   printf("saving window state, state = %p\n", (void*)state);
 #endif
 
   state.swap(*aState);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -781,17 +781,16 @@ protected:
   nsRefPtr<nsDOMWindowList>     mFrames;
   nsRefPtr<nsBarProp>           mMenubar;
   nsRefPtr<nsBarProp>           mToolbar;
   nsRefPtr<nsBarProp>           mLocationbar;
   nsRefPtr<nsBarProp>           mPersonalbar;
   nsRefPtr<nsBarProp>           mStatusbar;
   nsRefPtr<nsBarProp>           mScrollbars;
   nsCOMPtr<nsIWeakReference>    mWindowUtils;
-  nsRefPtr<nsLocation>          mLocation;
   nsString                      mStatus;
   nsString                      mDefaultStatus;
   // index 0->language_id 1, so index MAX-1 == language_id MAX
   nsGlobalWindowObserver*       mObserver;
 
   nsCOMPtr<nsIDOMCrypto>        mCrypto;
 
   nsCOMPtr<nsIDOMStorage>      mLocalStorage;
@@ -803,16 +802,17 @@ protected:
 
   // These member variable are used only on inner windows.
   nsCOMPtr<nsIEventListenerManager> mListenerManager;
   PRCList                       mTimeouts;
   // If mTimeoutInsertionPoint is non-null, insertions should happen after it.
   nsTimeout*                    mTimeoutInsertionPoint;
   PRUint32                      mTimeoutPublicIdCounter;
   PRUint32                      mTimeoutFiringDepth;
+  nsRefPtr<nsLocation>          mLocation;
 
   // Holder of the dummy java plugin, used to expose window.java and
   // window.packages.
   nsRefPtr<nsDummyJavaPluginOwner> mDummyJavaPluginOwner;
 
   // These member variables are used on both inner and the outer windows.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
   nsCOMPtr<nsIDocument> mDoc;  // For fast access to principals
--- a/dom/interfaces/core/nsIDOMNSDocument.idl
+++ b/dom/interfaces/core/nsIDOMNSDocument.idl
@@ -38,17 +38,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
 
 interface nsIBoxObject;
 interface nsIDOMLocation;
 
-[scriptable, uuid(42c27ca5-6065-42b6-ad9e-b748c1acd250)]
+[scriptable, uuid(7485b35e-d215-42a0-8a67-896f86c8afd9)]
 interface nsIDOMNSDocument : nsISupports
 {
   readonly attribute DOMString      characterSet;
            attribute DOMString      dir;
 
   readonly attribute nsIDOMLocation location;
 
            attribute DOMString      title;
@@ -92,9 +92,38 @@ interface nsIDOMNSDocument : nsISupports
   nsIDOMElement             elementFromPoint(in float x, in float y);
 
   /**
    * Release the current mouse capture if it is on an element within this
    * document.
    */
   void releaseCapture();
 
+  /**
+   * Use the given DOM element as the source image of target |-moz-element()|.
+   *
+   * This function introduces a new special ID (called "image element ID"),
+   * which is only used by |-moz-element()|, and associates it with the given
+   * DOM element.  Image elements ID's have the higher precedence than general
+   * HTML id's, so if |document.mozSetImageElement(<id>, <element>)| is called,
+   * |-moz-element(#<id>)| uses |<element>| as the source image even if there
+   * is another element with id attribute = |<id>|.  To unregister an image
+   * element ID |<id>|, call |document.mozSetImageElement(<id>, null)|.
+   *
+   * Example:
+   * <script>
+   *   canvas = document.createElement("canvas");
+   *   canvas.setAttribute("width", 100);
+   *   canvas.setAttribute("height", 100);
+   *   // draw to canvas
+   *   document.mozSetImageElement("canvasbg", canvas);
+   * </script>
+   * <div style="background-image: -moz-element(#canvasbg);"></div>
+   *
+   * @param aImageElementId an image element ID to associate with
+   * |aImageElement|
+   * @param aImageElement a DOM element to be used as the source image of
+   * |-moz-element(#aImageElementId)|. If this is null, the function will
+   * unregister the image element ID |aImageElementId|.
+   */
+  void mozSetImageElement(in DOMString aImageElementId,
+                          in nsIDOMElement aImageElement);
 };
--- a/dom/plugins/Makefile.in
+++ b/dom/plugins/Makefile.in
@@ -128,16 +128,17 @@ CPPSRCS = \
 
 ifeq (WINNT,$(OS_ARCH))
 CPPSRCS += COMMessageFilter.cpp
 endif
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 CMMSRCS   += \
     PluginUtilsOSX.mm \
+    PluginInterposeOSX.mm \
     $(NULL)
 endif
 
 LOCAL_INCLUDES = \
   -I$(topsrcdir)/modules/plugin/base/public/ \
   -I$(topsrcdir)/modules/plugin/base/src/ \
   $(NULL)
 
--- a/dom/plugins/PPluginModule.ipdl
+++ b/dom/plugins/PPluginModule.ipdl
@@ -95,12 +95,19 @@ parent:
   // native events, then "livelock" and some other glitches can occur.
   rpc ProcessSomeEvents();
 
   // Window-specific message which instructs the RPC mechanism to enter
   // a nested event loop for the current RPC call.
   async ProcessNativeEventsInRPCCall();
 
   sync AppendNotesToCrashReport(nsCString aNotes);
+
+  // OS X Specific calls to manage the plugin's window
+  // when interposing system calls.
+  async PluginShowWindow(uint32_t aWindowId, bool aModal,
+                         int32_t aX, int32_t aY,
+                         size_t aWidth, size_t aHeight);
+  async PluginHideWindow(uint32_t aWindowId);
 };
 
 } // namespace plugins
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PluginInterposeOSX.h
@@ -0,0 +1,45 @@
+// vim:set ts=2 sts=2 sw=2 et cin:
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+namespace mac_plugin_interposing {
+
+namespace parent {
+
+void OnPluginShowWindow(uint32_t window_id, CGRect window_bounds, bool modal);
+void OnPluginHideWindow(uint32_t window_id, pid_t aPluginPid);
+
+}
+
+namespace child {
+
+void SetUpCocoaInterposing();
+
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PluginInterposeOSX.mm
@@ -0,0 +1,293 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:set ts=2 sts=2 sw=2 et cin:
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/basictypes.h"
+#include "nsCocoaUtils.h"
+#include "PluginModuleChild.h"
+#include "nsDebug.h"
+#include <set>
+#import <AppKit/AppKit.h>
+#import <objc/runtime.h>
+
+using mozilla::plugins::PluginModuleChild;
+using mozilla::plugins::AssertPluginThread;
+
+namespace mac_plugin_interposing {
+namespace parent {
+
+// Tracks plugin windows currently visible.
+std::set<uint32_t> plugin_visible_windows_set_;
+// Tracks full screen windows currently visible.
+std::set<uint32_t> plugin_fullscreen_windows_set_;
+// Tracks modal windows currently visible.
+std::set<uint32_t> plugin_modal_windows_set_;
+
+void OnPluginShowWindow(uint32_t window_id,
+                        CGRect window_bounds,
+                        bool modal) {
+  plugin_visible_windows_set_.insert(window_id);
+
+  if (modal)
+    plugin_modal_windows_set_.insert(window_id);
+
+  CGRect main_display_bounds = ::CGDisplayBounds(CGMainDisplayID());
+
+  if (CGRectEqualToRect(window_bounds, main_display_bounds) &&
+      (plugin_fullscreen_windows_set_.find(window_id) ==
+       plugin_fullscreen_windows_set_.end())) {
+    plugin_fullscreen_windows_set_.insert(window_id);
+
+    nsCocoaUtils::HideOSChromeOnScreen(TRUE, [[NSScreen screens] objectAtIndex:0]);
+  }
+}
+
+static void ActivateProcess(pid_t pid) {
+  ProcessSerialNumber process;
+  OSStatus status = ::GetProcessForPID(pid, &process);
+
+  if (status == noErr) {
+    SetFrontProcess(&process);
+  } else {
+    NS_WARNING("Unable to get process for pid.");
+  }
+}
+
+// Must be called on the UI thread.
+// If plugin_pid is -1, the browser will be the active process on return,
+// otherwise that process will be given focus back before this function returns.
+static void ReleasePluginFullScreen(pid_t plugin_pid) {
+  // Releasing full screen only works if we are the frontmost process; grab
+  // focus, but give it back to the plugin process if requested.
+  ActivateProcess(base::GetCurrentProcId());
+
+  nsCocoaUtils::HideOSChromeOnScreen(FALSE, [[NSScreen screens] objectAtIndex:0]);
+
+  if (plugin_pid != -1) {
+    ActivateProcess(plugin_pid);
+  }
+}
+
+void OnPluginHideWindow(uint32_t window_id, pid_t aPluginPid) {
+  bool had_windows = !plugin_visible_windows_set_.empty();
+  plugin_visible_windows_set_.erase(window_id);
+  bool browser_needs_activation = had_windows &&
+      plugin_visible_windows_set_.empty();
+
+  plugin_modal_windows_set_.erase(window_id);
+  if (plugin_fullscreen_windows_set_.find(window_id) !=
+      plugin_fullscreen_windows_set_.end()) {
+    plugin_fullscreen_windows_set_.erase(window_id);
+    pid_t plugin_pid = browser_needs_activation ? -1 : aPluginPid;
+    browser_needs_activation = false;
+    ReleasePluginFullScreen(plugin_pid);
+  }
+
+  if (browser_needs_activation) {
+    ActivateProcess(getpid());
+  }
+}
+
+} // parent
+} // namespace mac_plugin_interposing
+
+namespace mac_plugin_interposing {
+namespace child {
+
+// TODO(stuartmorgan): Make this an IPC to order the plugin process above the
+// browser process only if the browser is current frontmost.
+void FocusPluginProcess() {
+  ProcessSerialNumber this_process, front_process;
+  if ((GetCurrentProcess(&this_process) != noErr) ||
+      (GetFrontProcess(&front_process) != noErr)) {
+    return;
+  }
+
+  Boolean matched = false;
+  if ((SameProcess(&this_process, &front_process, &matched) == noErr) &&
+      !matched) {
+    SetFrontProcess(&this_process);
+  }
+}
+
+void NotifyBrowserOfPluginShowWindow(uint32_t window_id, CGRect bounds,
+                                     bool modal) {
+  AssertPluginThread();
+
+  PluginModuleChild *pmc = PluginModuleChild::current();
+  if (pmc)
+    pmc->PluginShowWindow(window_id, modal, bounds);
+}
+
+void NotifyBrowserOfPluginHideWindow(uint32_t window_id, CGRect bounds) {
+  AssertPluginThread();
+
+  PluginModuleChild *pmc = PluginModuleChild::current();
+  if (pmc)
+    pmc->PluginHideWindow(window_id);
+}
+
+struct WindowInfo {
+  uint32_t window_id;
+  CGRect bounds;
+  WindowInfo(NSWindow* window) {
+    NSInteger window_num = [window windowNumber];
+    window_id = window_num > 0 ? window_num : 0;
+    bounds = NSRectToCGRect([window frame]);
+  }
+};
+
+static void OnPluginWindowClosed(const WindowInfo& window_info) {
+  if (window_info.window_id == 0)
+    return;
+  mac_plugin_interposing::child::NotifyBrowserOfPluginHideWindow(window_info.window_id,
+                                                                 window_info.bounds);
+}
+
+static void OnPluginWindowShown(const WindowInfo& window_info, BOOL is_modal) {
+  // The window id is 0 if it has never been shown (including while it is the
+  // process of being shown for the first time); when that happens, we'll catch
+  // it in _setWindowNumber instead.
+  static BOOL s_pending_display_is_modal = NO;
+  if (window_info.window_id == 0) {
+    if (is_modal)
+      s_pending_display_is_modal = YES;
+    return;
+  }
+  if (s_pending_display_is_modal) {
+    is_modal = YES;
+    s_pending_display_is_modal = NO;
+  }
+  mac_plugin_interposing::child::NotifyBrowserOfPluginShowWindow(
+    window_info.window_id, window_info.bounds, is_modal);
+}
+
+} // child
+} // namespace mac_plugin_interposing
+
+using mac_plugin_interposing::child::WindowInfo;
+
+@interface NSWindow (PluginInterposing)
+- (void)pluginInterpose_orderOut:(id)sender;
+- (void)pluginInterpose_orderFront:(id)sender;
+- (void)pluginInterpose_makeKeyAndOrderFront:(id)sender;
+- (void)pluginInterpose_setWindowNumber:(NSInteger)num;
+@end
+
+@implementation NSWindow (PluginInterposing)
+
+- (void)pluginInterpose_orderOut:(id)sender {
+  WindowInfo window_info(self);
+  [self pluginInterpose_orderOut:sender];
+  OnPluginWindowClosed(window_info);
+}
+
+- (void)pluginInterpose_orderFront:(id)sender {
+  mac_plugin_interposing::child::FocusPluginProcess();
+  [self pluginInterpose_orderFront:sender];
+  OnPluginWindowShown(WindowInfo(self), NO);
+}
+
+- (void)pluginInterpose_makeKeyAndOrderFront:(id)sender {
+  mac_plugin_interposing::child::FocusPluginProcess();
+  [self pluginInterpose_makeKeyAndOrderFront:sender];
+  OnPluginWindowShown(WindowInfo(self), NO);
+}
+
+- (void)pluginInterpose_setWindowNumber:(NSInteger)num {
+  if (num > 0)
+    mac_plugin_interposing::child::FocusPluginProcess();
+  [self pluginInterpose_setWindowNumber:num];
+  if (num > 0)
+    OnPluginWindowShown(WindowInfo(self), NO);
+}
+
+@end
+
+@interface NSApplication (PluginInterposing)
+- (NSInteger)pluginInterpose_runModalForWindow:(NSWindow*)window;
+@end
+
+@implementation NSApplication (PluginInterposing)
+
+- (NSInteger)pluginInterpose_runModalForWindow:(NSWindow*)window {
+  mac_plugin_interposing::child::FocusPluginProcess();
+  // This is out-of-order relative to the other calls, but runModalForWindow:
+  // won't return until the window closes, and the order only matters for
+  // full-screen windows.
+  OnPluginWindowShown(WindowInfo(window), YES);
+  return [self pluginInterpose_runModalForWindow:window];
+}
+
+@end
+
+static void ExchangeMethods(Class target_class,
+                            BOOL class_method,
+                            SEL original,
+                            SEL replacement) {
+  Method m1;
+  Method m2;
+  if (class_method) {
+    m1 = class_getClassMethod(target_class, original);
+    m2 = class_getClassMethod(target_class, replacement);
+  } else {
+    m1 = class_getInstanceMethod(target_class, original);
+    m2 = class_getInstanceMethod(target_class, replacement);
+  }
+
+  if (m1 == m2)
+    return;
+
+  if (m1 && m2)
+    method_exchangeImplementations(m1, m2);
+  else
+    NS_NOTREACHED("Cocoa swizzling failed");
+}
+
+namespace mac_plugin_interposing {
+namespace child {
+
+void SetUpCocoaInterposing() {
+  Class nswindow_class = [NSWindow class];
+  ExchangeMethods(nswindow_class, NO, @selector(orderOut:),
+                  @selector(pluginInterpose_orderOut:));
+  ExchangeMethods(nswindow_class, NO, @selector(orderFront:),
+                  @selector(pluginInterpose_orderFront:));
+  ExchangeMethods(nswindow_class, NO, @selector(makeKeyAndOrderFront:),
+                  @selector(pluginInterpose_makeKeyAndOrderFront:));
+  ExchangeMethods(nswindow_class, NO, @selector(_setWindowNumber:),
+                  @selector(pluginInterpose_setWindowNumber:));
+
+  ExchangeMethods([NSApplication class], NO, @selector(runModalForWindow:),
+                  @selector(pluginInterpose_runModalForWindow:));
+}
+
+}  // namespace child
+}  // namespace mac_plugin_interposing
+
--- a/dom/plugins/PluginModuleChild.cpp
+++ b/dom/plugins/PluginModuleChild.cpp
@@ -65,16 +65,17 @@
 
 #include "nsNPAPIPlugin.h"
 
 #ifdef XP_WIN
 #include "COMMessageFilter.h"
 #endif
 
 #ifdef OS_MACOSX
+#include "PluginInterposeOSX.h"
 #include "PluginUtilsOSX.h"
 #endif
 
 using namespace mozilla::plugins;
 
 #if defined(XP_WIN)
 const PRUnichar * kFlashFullscreenClass = L"ShockwaveFlashFullScreen";
 #endif
@@ -109,16 +110,19 @@ PluginModuleChild::PluginModuleChild() :
   , mNestedEventHook(NULL)
   , mGlobalCallWndProcHook(NULL)
 #endif
 {
     NS_ASSERTION(!gInstance, "Something terribly wrong here!");
     memset(&mFunctions, 0, sizeof(mFunctions));
     memset(&mSavedData, 0, sizeof(mSavedData));
     gInstance = this;
+#ifdef XP_MACOSX
+    mac_plugin_interposing::child::SetUpCocoaInterposing();
+#endif
 }
 
 PluginModuleChild::~PluginModuleChild()
 {
     NS_ASSERTION(gInstance == this, "Something terribly wrong here!");
     if (mLibrary) {
         PR_UnloadLibrary(mLibrary);
     }
@@ -184,17 +188,17 @@ PluginModuleChild::Init(const std::strin
     pluginFile->Exists(&exists);
     NS_ASSERTION(exists, "plugin file ain't there");
 
     nsCOMPtr<nsIFile> pluginIfile;
     pluginIfile = do_QueryInterface(pluginFile);
 
     nsPluginFile lib(pluginIfile);
 
-    nsresult rv = lib.LoadPlugin(&mLibrary);
+    nsresult rv = lib.LoadPlugin(mLibrary);
     NS_ASSERTION(NS_OK == rv, "trouble with mPluginFile");
     NS_ASSERTION(mLibrary, "couldn't open shared object");
 
     if (!Open(aChannel, aParentProcessHandle, aIOLoop))
         return false;
 
     memset((void*) &mFunctions, 0, sizeof(mFunctions));
     mFunctions.size = sizeof(mFunctions);
--- a/dom/plugins/PluginModuleChild.h
+++ b/dom/plugins/PluginModuleChild.h
@@ -190,16 +190,24 @@ public:
                                                      NPIdentifier* aIdentifiers);
     static NPIdentifier NP_CALLBACK NPN_GetIntIdentifier(int32_t aIntId);
     static bool NP_CALLBACK NPN_IdentifierIsString(NPIdentifier aIdentifier);
     static NPUTF8* NP_CALLBACK NPN_UTF8FromIdentifier(NPIdentifier aIdentifier);
     static int32_t NP_CALLBACK NPN_IntFromIdentifier(NPIdentifier aIdentifier);
 
 #ifdef OS_MACOSX
     void ProcessNativeEvents();
+    
+    void PluginShowWindow(uint32_t window_id, bool modal, CGRect r) {
+        SendPluginShowWindow(window_id, modal, r.origin.x, r.origin.y, r.size.width, r.size.height);
+    }
+
+    void PluginHideWindow(uint32_t window_id) {
+        SendPluginHideWindow(window_id);
+    }
 #endif
 
 private:
     bool InitGraphics();
 #if defined(MOZ_WIDGET_GTK2)
     static gboolean DetectNestedEventLoop(gpointer data);
     static gboolean ProcessBrowserEvents(gpointer data);
 
--- a/dom/plugins/PluginModuleParent.cpp
+++ b/dom/plugins/PluginModuleParent.cpp
@@ -35,16 +35,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifdef MOZ_WIDGET_GTK2
 #include <glib.h>
 #elif XP_MACOSX
 #include "PluginUtilsOSX.h"
+#include "PluginInterposeOSX.h"
 #endif
 #ifdef MOZ_WIDGET_QT
 #include <QtCore/QCoreApplication>
 #include <QtCore/QEventLoop>
 #endif
 
 #include "base/process_util.h"
 
@@ -820,17 +821,48 @@ bool
 PluginModuleParent::RecvProcessNativeEventsInRPCCall()
 {
     PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
 #if defined(OS_WIN)
     ProcessNativeEventsInRPCCall();
     return true;
 #else
     NS_NOTREACHED(
-        "PluginInstanceParent::AnswerSetNestedEventState not implemented!");
+        "PluginInstanceParent::RecvProcessNativeEventsInRPCCall not implemented!");
+    return false;
+#endif
+}
+
+bool
+PluginModuleParent::RecvPluginShowWindow(const uint32_t& aWindowId, const bool& aModal,
+                                         const int32_t& aX, const int32_t& aY,
+                                         const size_t& aWidth, const size_t& aHeight)
+{
+    PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+    CGRect windowBound = ::CGRectMake(aX, aY, aWidth, aHeight);
+    mac_plugin_interposing::parent::OnPluginShowWindow(aWindowId, windowBound, aModal);
+    return true;
+#else
+    NS_NOTREACHED(
+        "PluginInstanceParent::RecvPluginShowWindow not implemented!");
+    return false;
+#endif
+}
+
+bool
+PluginModuleParent::RecvPluginHideWindow(const uint32_t& aWindowId)
+{
+    PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+    mac_plugin_interposing::parent::OnPluginHideWindow(aWindowId, OtherSidePID());
+    return true;
+#else
+    NS_NOTREACHED(
+        "PluginInstanceParent::RecvPluginHideWindow not implemented!");
     return false;
 #endif
 }
 
 #ifdef OS_MACOSX
 #define DEFAULT_REFRESH_MS 20 // CoreAnimation: 50 FPS
 void
 PluginModuleParent::AddToRefreshTimer(PluginInstanceParent *aInstance) {
--- a/dom/plugins/PluginModuleParent.h
+++ b/dom/plugins/PluginModuleParent.h
@@ -166,16 +166,24 @@ protected:
     virtual bool AnswerProcessSomeEvents();
 
     NS_OVERRIDE virtual bool
     RecvProcessNativeEventsInRPCCall();
 
     virtual bool
     RecvAppendNotesToCrashReport(const nsCString& aNotes);
 
+    NS_OVERRIDE virtual bool
+    RecvPluginShowWindow(const uint32_t& aWindowId, const bool& aModal,
+                         const int32_t& aX, const int32_t& aY,
+                         const size_t& aWidth, const size_t& aHeight);
+
+    NS_OVERRIDE virtual bool
+    RecvPluginHideWindow(const uint32_t& aWindowId);
+
     static PluginInstanceParent* InstCast(NPP instance);
     static BrowserStreamParent* StreamCast(NPP instance, NPStream* s);
 
 private:
     void SetPluginFuncs(NPPluginFuncs* aFuncs);
 
     // Implement the module-level functions from NPAPI; these are
     // normally resolved directly from the DSO.
--- a/dom/tests/mochitest/dom-level0/Makefile.in
+++ b/dom/tests/mochitest/dom-level0/Makefile.in
@@ -46,12 +46,14 @@ include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES	= \
 		test_crossdomainprops.html \
 		file_crossdomainprops_inner.html \
 		test_setting_document.domain_to_shortened_ipaddr.html \
 		child_ip_address.html \
 		test_setting_document.domain_idn.html \
 		idn_child.html \
+		file_location.html \
+		test_location.html \
 		$(NULL)
 
 libs:: 	$(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/dom-level0/file_location.html
@@ -0,0 +1,10 @@
+<html>
+    <script>
+        try {
+            parent.SimpleTest.ok(!("existingprop" in location), "got a new location object in the iframe");
+        } catch (e) {
+        }
+
+        location.iframeprop = 42;
+    </script>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/dom-level0/test_location.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for location object behaviors</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<iframe id="ifr" onload="runTest()"></iframe>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var count = 0;
+var firstlocation;
+var lastlocation;
+
+function runTest() {
+  ++count;
+  if (count == 1) {
+    firstlocation = $('ifr').contentWindow.location;
+    firstlocation.existingprop = 'fail';
+    firstlocation.href = 'file_location.html';
+    return;
+  }
+
+  if (count == 2) {
+    lastlocation = $('ifr').contentWindow.location;
+    is(lastlocation.iframeprop, 42, 'can read the new prop');
+    ok(firstlocation !== lastlocation, 'got a new location object');
+    // firstlocation should still work.
+    firstlocation.href = 'http://example.com/tests/dom/tests/mochitest/dom-level0/file_location.html';
+    return;
+  }
+
+  if (count == 3) {
+    try {
+      isnot($('ifr').contentWindow.location.iframeprop, 42, "shouldn't see this");
+    } catch (e) {
+      ok(/Permission denied/.test(e), "correctly threw a security error");
+    }
+
+    // XXX this doesn't work - file a bug on me!
+    //firstlocation.href = 'http://mochi.test:8888/tests/dom/tests/mochitest/dom-level0/file_location.html';
+    todo(false, 'can use old same-origin location object to set cross-origin frame');
+    $('ifr').contentWindow.location = 'file_location.html';
+    return;
+  }
+
+  is(lastlocation.iframeprop, 42, 'can still read old values of the location object');
+  ok(lastlocation !== $('ifr').contentWindow.location, 'location objects are distinct');
+
+  SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c
+++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
@@ -110,28 +110,27 @@ CG_EXTERN CGSize CGContextGetPatternPhas
 typedef uint32_t CGBitmapInfo;
 
 /* public in 10.4, present in 10.3.9 */
 CG_EXTERN void CGContextReplacePathWithStrokedPath (CGContextRef);
 CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
 #endif
 
 /* Some of these are present in earlier versions of the OS than where
- * they are public; others are not public at all (CGContextCopyPath,
- * CGContextReplacePathWithClipPath, many of the getters, etc.)
+ * they are public; others are not public at all
+ * (CGContextReplacePathWithClipPath, many of the getters, etc.)
  */
 static void (*CGContextClipToMaskPtr) (CGContextRef, CGRect, CGImageRef) = NULL;
 static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
 static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
 static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL;
 static bool (*CGContextGetShouldAntialiasFontsPtr) (CGContextRef) = NULL;
 static bool (*CGContextGetShouldSmoothFontsPtr) (CGContextRef) = NULL;
 static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
 static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
-static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
 static void (*CGContextReplacePathWithClipPathPtr) (CGContextRef) = NULL;
 
 static SInt32 _cairo_quartz_osx_version = 0x0;
 
 static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
 
 /*
  * Utility functions
@@ -155,17 +154,16 @@ static void quartz_ensure_symbols(void)
 	return;
 
     CGContextClipToMaskPtr = dlsym(RTLD_DEFAULT, "CGContextClipToMask");
     CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage");
     CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType");
     CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts");
     CGContextGetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextGetShouldAntialiasFonts");
     CGContextGetShouldSmoothFontsPtr = dlsym(RTLD_DEFAULT, "CGContextGetShouldSmoothFonts");
-    CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath");
     CGContextReplacePathWithClipPathPtr = dlsym(RTLD_DEFAULT, "CGContextReplacePathWithClipPath");
     CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
     CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
 
     if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) {
 	// assume 10.4
 	_cairo_quartz_osx_version = 0x1040;
     }
@@ -609,162 +607,16 @@ static inline void
     dst->a = src->xx;
     dst->b = src->yx;
     dst->c = src->xy;
     dst->d = src->yy;
     dst->tx = src->x0;
     dst->ty = src->y0;
 }
 
-typedef struct {
-    bool isClipping;
-    CGGlyph *cg_glyphs;
-    CGSize *cg_advances;
-    size_t nglyphs;
-    CGAffineTransform textTransform;
-    CGFontRef font;
-    CGPoint origin;
-} unbounded_show_glyphs_t;
-
-typedef struct {
-    CGPathRef cgPath;
-    cairo_fill_rule_t fill_rule;
-} unbounded_stroke_fill_t;
-
-typedef struct {
-    CGImageRef mask;
-    CGAffineTransform maskTransform;
-} unbounded_mask_t;
-
-typedef enum {
-    UNBOUNDED_STROKE_FILL,
-    UNBOUNDED_SHOW_GLYPHS,
-    UNBOUNDED_MASK
-} unbounded_op_t;
-
-typedef struct {
-    unbounded_op_t op;
-    union {
-	unbounded_stroke_fill_t stroke_fill;
-	unbounded_show_glyphs_t show_glyphs;
-	unbounded_mask_t mask;
-    } u;
-} unbounded_op_data_t;
-
-static void
-_cairo_quartz_fixup_unbounded_operation (cairo_quartz_surface_t *surface,
-					 unbounded_op_data_t *op,
-					 cairo_antialias_t antialias)
-{
-    CGRect clipBox, clipBoxRound;
-    CGContextRef cgc;
-    CGImageRef maskImage;
-
-    /* TODO: handle failure */
-    if (!CGContextClipToMaskPtr)
-	return;
-
-    clipBox = CGContextGetClipBoundingBox (surface->cgContext);
-    clipBoxRound = CGRectIntegral (clipBox);
-
-    cgc = CGBitmapContextCreate (NULL,
-				 clipBoxRound.size.width,
-				 clipBoxRound.size.height,
-				 8,
-				 (((size_t) clipBoxRound.size.width) + 15) & (~15),
-				 NULL,
-				 kCGImageAlphaOnly);
-
-    if (!cgc)
-	return;
-
-    CGContextSetCompositeOperation (cgc, kPrivateCGCompositeCopy);
-    /* We want to mask out whatever we just rendered, so we fill the
-     * surface opaque, and then we'll render transparent.
-     */
-    CGContextSetAlpha (cgc, 1.0f);
-    CGContextFillRect (cgc, CGRectMake (0, 0, clipBoxRound.size.width, clipBoxRound.size.height));
-
-    CGContextSetCompositeOperation (cgc, kPrivateCGCompositeClear);
-    CGContextSetShouldAntialias (cgc, (antialias != CAIRO_ANTIALIAS_NONE));
-
-    CGContextTranslateCTM (cgc, -clipBoxRound.origin.x, -clipBoxRound.origin.y);
-
-    /* We need to either render the path that was given to us, or the glyph op */
-    if (op->op == UNBOUNDED_STROKE_FILL) {
-	CGContextBeginPath (cgc);
-	CGContextAddPath (cgc, op->u.stroke_fill.cgPath);
-
-	if (op->u.stroke_fill.fill_rule == CAIRO_FILL_RULE_WINDING)
-	    CGContextFillPath (cgc);
-	else
-	    CGContextEOFillPath (cgc);
-    } else if (op->op == UNBOUNDED_SHOW_GLYPHS) {
-	CGContextSetFont (cgc, op->u.show_glyphs.font);
-	CGContextSetFontSize (cgc, 1.0);
-	CGContextSetTextMatrix (cgc, op->u.show_glyphs.textTransform);
-	CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
-
-	if (op->u.show_glyphs.isClipping) {
-	    /* Note that the comment in show_glyphs about kCGTextClip
-	     * and the text transform still applies here; however, the
-	     * cg_advances we have were already transformed, so we
-	     * don't have to do anything. */
-	    CGContextSetTextDrawingMode (cgc, kCGTextClip);
-	    CGContextSaveGState (cgc);
-	}
-
-	CGContextShowGlyphsWithAdvances (cgc,
-					 op->u.show_glyphs.cg_glyphs,
-					 op->u.show_glyphs.cg_advances,
-					 op->u.show_glyphs.nglyphs);
-
-	if (op->u.show_glyphs.isClipping) {
-	    CGContextClearRect (cgc, clipBoxRound);
-	    CGContextRestoreGState (cgc);
-	}
-    } else if (op->op == UNBOUNDED_MASK) {
-	CGAffineTransform ctm = CGContextGetCTM (cgc);
-	CGContextSaveGState (cgc);
-	CGContextConcatCTM (cgc, op->u.mask.maskTransform);
-	CGContextClipToMask (cgc, CGRectMake (0.0f, 0.0f,
-					      CGImageGetWidth(op->u.mask.mask), CGImageGetHeight(op->u.mask.mask)),
-			     op->u.mask.mask);
-	CGContextSetCTM (cgc, ctm);
-	CGContextClearRect (cgc, clipBoxRound);
-	CGContextRestoreGState (cgc);
-    }
-
-    /* Also mask out the portion of the clipbox that we rounded out, if any */
-    if (!CGRectEqualToRect (clipBox, clipBoxRound)) {
-	CGContextBeginPath (cgc);
-	CGContextAddRect (cgc, clipBoxRound);
-	CGContextAddRect (cgc, clipBox);
-	CGContextEOFillPath (cgc);
-    }
-
-    maskImage = CGBitmapContextCreateImage (cgc);
-    CGContextRelease (cgc);
-
-    if (!maskImage)
-	return;
-
-    /* Then render with the mask */
-    CGContextSaveGState (surface->cgContext);
-
-    CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeCopy);
-    CGContextClipToMaskPtr (surface->cgContext, clipBoxRound, maskImage);
-    CGImageRelease (maskImage);
-
-    /* Finally, clear out the entire clipping region through our mask */
-    CGContextClearRect (surface->cgContext, clipBoxRound);
-
-    CGContextRestoreGState (surface->cgContext);
-}
-
 /*
  * Source -> Quartz setup and finish functions
  */
 
 static void
 ComputeGradientValue (void *info, const CGFloat *in, CGFloat *out)
 {
     double fdist = *in;
@@ -1366,16 +1218,21 @@ typedef struct {
     // Used with DO_LAYER
     CGLayerRef layer;
 
     // Used with DO_SHADING
     CGShadingRef shading;
 
     // Used with DO_PATTERN
     CGPatternRef pattern;
+
+    // Used for handling unbounded operators
+    CGLayerRef unboundedLayer;
+    CGPoint unboundedLayerOffset;
+    CGContextRef unboundedDestination;
 } cairo_quartz_drawing_state_t;
 
 static void
 _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
 				     const cairo_pattern_t *source,
 				     cairo_quartz_drawing_state_t *state)
 {
     CGRect clipBox = CGContextGetClipBoundingBox (state->context);
@@ -1624,44 +1481,71 @@ static void
 static cairo_quartz_drawing_state_t
 _cairo_quartz_setup_state (cairo_quartz_surface_t *surface,
 			   const cairo_pattern_t *source,
 			   cairo_operator_t op,
 			   cairo_rectangle_int_t *extents)
 {
     CGContextRef context = surface->cgContext;
     cairo_quartz_drawing_state_t state;
+    cairo_bool_t bounded_op = _cairo_operator_bounded_by_mask (op);
     cairo_status_t status;
 
     state.context = context;
     state.image = NULL;
     state.imageSurface = NULL;
     state.layer = NULL;
     state.shading = NULL;
     state.pattern = NULL;
+    state.unboundedLayer = NULL;
 
     _cairo_quartz_surface_will_change (surface);
 
     // Save before we change the pattern, colorspace, etc. so that
     // we can restore and make sure that quartz releases our
     // pattern (which may be stack allocated)
     CGContextSaveGState(context);
 
-    CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter));
-
     status = _cairo_quartz_surface_set_cairo_operator (surface, op);
     if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
         state.action = DO_NOTHING;
         return state;
     }
     if (status) {
         state.action = DO_UNSUPPORTED;
         return state;
     }
 
+    if (!bounded_op) {
+        state.unboundedDestination = context;
+
+        CGRect clipBox = CGContextGetClipBoundingBox (context);
+        CGRect clipBoxRound = CGRectIntegral (CGRectInset (clipBox, -1, -1));
+        state.unboundedLayer = CGLayerCreateWithContext (context, clipBoxRound.size, NULL);
+        if (!state.unboundedLayer) {
+            state.action = DO_UNSUPPORTED;
+            return state;
+        }
+
+        context = CGLayerGetContext (state.unboundedLayer);
+        if (!context) {
+            state.action = DO_UNSUPPORTED;
+            return state;
+        }
+        state.context = context;
+        // No need to save state here, since this context won't be used later
+        CGContextTranslateCTM (context, -clipBoxRound.origin.x, -clipBoxRound.origin.y);
+
+        state.unboundedLayerOffset = clipBoxRound.origin;
+
+        CGContextSetCompositeOperation (context, kPrivateCGCompositeCopy);
+    }
+
+    CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter));
+
     if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
 	cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
 
 	CGContextSetRGBStrokeColor (context,
 				    solid->color.red,
 				    solid->color.green,
 				    solid->color.blue,
 				    solid->color.alpha);
@@ -1808,43 +1692,52 @@ static cairo_quartz_drawing_state_t
     }
 
     state.action = DO_UNSUPPORTED;
     return state;
 }
 
 /**
  * 1) Tears down internal state used to draw the source
- * 2) Does CGContextRestoreGState(state->context)
+ * 2) Does CGContextRestoreGState on the state saved by _cairo_quartz_setup_state
  */
 static void
 _cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state)
 {
     if (state->image) {
-	CGImageRelease(state->image);
+	CGImageRelease (state->image);
     }
 
     if (state->imageSurface) {
-	cairo_surface_destroy(state->imageSurface);
+	cairo_surface_destroy (state->imageSurface);
     }
 
     if (state->shading) {
-	CGShadingRelease(state->shading);
+	CGShadingRelease (state->shading);
     }
 
     if (state->pattern) {
-	CGPatternRelease(state->pattern);
+	CGPatternRelease (state->pattern);
     }
 
-    CGContextRestoreGState(state->context);
+    if (state->unboundedLayer) {
+        // Copy the layer back to the destination
+        CGContextDrawLayerAtPoint (state->unboundedDestination,
+                                   state->unboundedLayerOffset,
+                                   state->unboundedLayer);
+        CGContextRestoreGState (state->unboundedDestination);
+        CGLayerRelease (state->unboundedLayer);
+    } else {
+        CGContextRestoreGState (state->context);
+    }
 }
 
 
 static void
-_cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op)
+_cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state)
 {
     assert (state &&
             ((state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE)) ||
              (state->layer && state->action == DO_LAYER)));
 
     CGContextConcatCTM (state->context, state->transform);
     CGContextTranslateCTM (state->context, 0, state->imageRect.size.height);
     CGContextScaleCTM (state->context, 1, -1);
@@ -1859,24 +1752,16 @@ static void
              * to draw a CGLayer to any CGContext, even one it wasn't
              * created for.
              */
             CGContextDrawLayerAtPoint (state->context, state->imageRect.origin,
                                        state->layer);
         } else {
             CGContextDrawImage (state->context, state->imageRect, state->image);
         }
-
-	if (!_cairo_operator_bounded_by_source (op)) {
-	    CGContextBeginPath (state->context);
-	    CGContextAddRect (state->context, state->imageRect);
-	    CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context));
-	    CGContextSetRGBFillColor (state->context, 0, 0, 0, 0);
-	    CGContextEOFillPath (state->context);
-	}
     }
 }
 
 
 /*
  * get source/dest image implementation
  */
 
@@ -2256,16 +2141,38 @@ static cairo_bool_t
 {
     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
     *extents = surface->extents;
     return TRUE;
 }
 
 static cairo_int_status_t
+_cairo_quartz_surface_paint_internal (cairo_quartz_surface_t *surface,
+			              cairo_quartz_drawing_state_t *state)
+{
+    if (state->action == DO_SOLID || state->action == DO_PATTERN) {
+	CGContextFillRect (state->context, CGRectMake(surface->extents.x,
+						      surface->extents.y,
+						      surface->extents.width,
+						      surface->extents.height));
+    } else if (state->action == DO_SHADING) {
+	CGContextConcatCTM (state->context, state->transform);
+	CGContextDrawShading (state->context, state->shading);
+    } else if (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE ||
+               state->action == DO_LAYER) {
+	_cairo_quartz_draw_image (state);
+    } else if (state->action != DO_NOTHING) {
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
 _cairo_quartz_surface_paint (void *abstract_surface,
 			     cairo_operator_t op,
 			     const cairo_pattern_t *source,
 			     cairo_clip_t *clip)
 {
     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
     cairo_quartz_drawing_state_t state;
@@ -2276,30 +2183,17 @@ static cairo_int_status_t
 	return CAIRO_STATUS_SUCCESS;
 
     rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     if (unlikely (rv))
 	return rv;
 
     state = _cairo_quartz_setup_state (surface, source, op, NULL);
 
-    if (state.action == DO_SOLID || state.action == DO_PATTERN) {
-	CGContextFillRect (state.context, CGRectMake(surface->extents.x,
-						     surface->extents.y,
-						     surface->extents.width,
-						     surface->extents.height));
-    } else if (state.action == DO_SHADING) {
-	CGContextConcatCTM (state.context, state.transform);
-	CGContextDrawShading (state.context, state.shading);
-    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
-               state.action == DO_LAYER) {
-	_cairo_quartz_draw_image (&state, op);
-    } else if (state.action != DO_NOTHING) {
-	rv = CAIRO_INT_STATUS_UNSUPPORTED;
-    }
+    rv = _cairo_quartz_surface_paint_internal (surface, &state);
 
     _cairo_quartz_teardown_state (&state);
 
     ND((stderr, "-- paint\n"));
     return rv;
 }
 
 static cairo_bool_t
@@ -2330,17 +2224,16 @@ static cairo_int_status_t
 			     double tolerance,
 			     cairo_antialias_t antialias,
 			     cairo_clip_t *clip)
 {
     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
     cairo_quartz_drawing_state_t state;
     quartz_stroke_t stroke;
-    CGPathRef path_for_unbounded = NULL;
 
     ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
 
     if (IS_EMPTY(surface))
 	return CAIRO_STATUS_SUCCESS;
 
     rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     if (unlikely (rv))
@@ -2363,19 +2256,16 @@ static cairo_int_status_t
     CGContextBeginPath (state.context);
 
     stroke.cgContext = state.context;
     stroke.ctm_inverse = NULL;
     rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
     if (rv)
         goto BAIL;
 
-    if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr)
-	path_for_unbounded = CGContextCopyPathPtr (state.context);
-
     if (state.action == DO_SOLID || state.action == DO_PATTERN) {
 	if (fill_rule == CAIRO_FILL_RULE_WINDING)
 	    CGContextFillPath (state.context);
 	else
 	    CGContextEOFillPath (state.context);
     } else if (state.action == DO_SHADING) {
 
 	// we have to clip and then paint the shading; we can't fill
@@ -2389,34 +2279,24 @@ static cairo_int_status_t
 	CGContextDrawShading (state.context, state.shading);
     } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
                state.action == DO_LAYER) {
 	if (fill_rule == CAIRO_FILL_RULE_WINDING)
 	    CGContextClip (state.context);
 	else
 	    CGContextEOClip (state.context);
 
-	_cairo_quartz_draw_image (&state, op);
+	_cairo_quartz_draw_image (&state);
     } else if (state.action != DO_NOTHING) {
 	rv = CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
   BAIL:
     _cairo_quartz_teardown_state (&state);
 
-    if (path_for_unbounded) {
-	unbounded_op_data_t ub;
-	ub.op = UNBOUNDED_STROKE_FILL;
-	ub.u.stroke_fill.cgPath = path_for_unbounded;
-	ub.u.stroke_fill.fill_rule = fill_rule;
-
-	_cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
-	CGPathRelease (path_for_unbounded);
-    }
-
     ND((stderr, "-- fill\n"));
     return rv;
 }
 
 static cairo_int_status_t
 _cairo_quartz_surface_stroke (void *abstract_surface,
 			      cairo_operator_t op,
 			      const cairo_pattern_t *source,
@@ -2428,17 +2308,16 @@ static cairo_int_status_t
 			      cairo_antialias_t antialias,
 			      cairo_clip_t *clip)
 {
     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
     cairo_quartz_drawing_state_t state;
     quartz_stroke_t stroke;
     CGAffineTransform origCTM, strokeTransform;
-    CGPathRef path_for_unbounded = NULL;
 
     ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
 
     if (IS_EMPTY(surface))
 	return CAIRO_STATUS_SUCCESS;
 
     rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     if (unlikely (rv))
@@ -2503,57 +2382,38 @@ static cairo_int_status_t
     CGContextBeginPath (state.context);
 
     stroke.cgContext = state.context;
     stroke.ctm_inverse = ctm_inverse;
     rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
     if (rv)
 	goto BAIL;
 
-    if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
-	path_for_unbounded = CGContextCopyPathPtr (state.context);
-
     if (state.action == DO_SOLID || state.action == DO_PATTERN) {
 	CGContextStrokePath (state.context);
     } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
                state.action == DO_LAYER) {
 	CGContextReplacePathWithStrokedPath (state.context);
 	CGContextClip (state.context);
 
 	CGContextSetCTM (state.context, origCTM);
-	_cairo_quartz_draw_image (&state, op);
+	_cairo_quartz_draw_image (&state);
     } else if (state.action == DO_SHADING) {
 	CGContextReplacePathWithStrokedPath (state.context);
 	CGContextClip (state.context);
 
 	CGContextSetCTM (state.context, origCTM);
 
 	CGContextConcatCTM (state.context, state.transform);
 	CGContextDrawShading (state.context, state.shading);
     } else if (state.action != DO_NOTHING) {
 	rv = CAIRO_INT_STATUS_UNSUPPORTED;
 	goto BAIL;
     }
 
-    if (path_for_unbounded) {
-	CGContextSetCTM (state.context, origCTM);
-	CGContextConcatCTM (state.context, strokeTransform);
-
-	CGContextBeginPath (state.context);
-	CGContextAddPath (state.context, path_for_unbounded);
-	CGPathRelease (path_for_unbounded);
-
-	CGContextReplacePathWithStrokedPath (state.context);
-
-	CGContextAddRect (state.context, CGContextGetClipBoundingBox (state.context));
-
-	CGContextSetRGBFillColor (state.context, 0., 0., 0., 0.);
-	CGContextEOFillPath (state.context);
-    }
-
   BAIL:
     _cairo_quartz_teardown_state (&state);
 
     ND((stderr, "-- stroke\n"));
     return rv;
 }
 
 #if CAIRO_HAS_QUARTZ_FONT
@@ -2718,47 +2578,28 @@ static cairo_int_status_t
 				     cg_glyphs,
 				     cg_advances,
 				     num_glyphs);
 
     CGContextSetCTM (state.context, ctm);
 
     if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
         state.action == DO_LAYER) {
-	_cairo_quartz_draw_image (&state, op);
+	_cairo_quartz_draw_image (&state);
     } else if (state.action == DO_SHADING) {
 	CGContextConcatCTM (state.context, state.transform);
 	CGContextDrawShading (state.context, state.shading);
     }
 
 BAIL:
     if (didForceFontSmoothing)
         CGContextSetAllowsFontSmoothingPtr (state.context, FALSE);
 
     _cairo_quartz_teardown_state (&state);
 
-    if (rv == CAIRO_STATUS_SUCCESS &&
-	cgfref &&
-	!_cairo_operator_bounded_by_mask (op))
-    {
-	unbounded_op_data_t ub;
-	ub.op = UNBOUNDED_SHOW_GLYPHS;
-
-	ub.u.show_glyphs.isClipping = isClipping;
-	ub.u.show_glyphs.cg_glyphs = cg_glyphs;
-	ub.u.show_glyphs.cg_advances = cg_advances;
-	ub.u.show_glyphs.nglyphs = num_glyphs;
-	ub.u.show_glyphs.textTransform = textTransform;
-	ub.u.show_glyphs.font = cgfref;
-	ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y);
-
-	_cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias);
-    }
-
-
     if (cg_advances != &cg_advances_static[0]) {
 	free (cg_advances);
     }
 
     if (cg_glyphs != &glyphs_static[0]) {
 	free (cg_glyphs);
     }
 
@@ -2773,55 +2614,54 @@ static cairo_int_status_t
                                          const cairo_surface_pattern_t *mask,
 					 cairo_clip_t *clip)
 {
     CGRect rect;
     CGImageRef img;
     cairo_surface_t *pat_surf = mask->surface;
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
     CGAffineTransform ctm, mask_matrix;
+    cairo_quartz_drawing_state_t state;
+
+    if (IS_EMPTY(surface))
+	return CAIRO_STATUS_SUCCESS;
+
+    if (op == CAIRO_OPERATOR_DEST)
+	return CAIRO_STATUS_SUCCESS;
 
     status = _cairo_surface_to_cgimage (pat_surf, &img);
     if (status)
 	return status;
     if (img == NULL) {
 	if (!_cairo_operator_bounded_by_mask (op))
 	    CGContextClearRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
 	return CAIRO_STATUS_SUCCESS;
     }
 
     rect = CGRectMake (0.0f, 0.0f, CGImageGetWidth (img) , CGImageGetHeight (img));
 
-    CGContextSaveGState (surface->cgContext);
+    state = _cairo_quartz_setup_state (surface, source, op, NULL);
 
     /* ClipToMask is essentially drawing an image, so we need to flip the CTM
      * to get the image to appear oriented the right way */
-    ctm = CGContextGetCTM (surface->cgContext);
+    ctm = CGContextGetCTM (state.context);
 
     _cairo_quartz_cairo_matrix_to_quartz (&mask->base.matrix, &mask_matrix);
     mask_matrix = CGAffineTransformInvert(mask_matrix);
     mask_matrix = CGAffineTransformTranslate (mask_matrix, 0.0, CGImageGetHeight (img));
     mask_matrix = CGAffineTransformScale (mask_matrix, 1.0, -1.0);
 
-    CGContextConcatCTM (surface->cgContext, mask_matrix);
-    CGContextClipToMaskPtr (surface->cgContext, rect, img);
-
-    CGContextSetCTM (surface->cgContext, ctm);
-
-    status = _cairo_quartz_surface_paint (surface, op, source, clip);
-
-    CGContextRestoreGState (surface->cgContext);
-
-    if (!_cairo_operator_bounded_by_mask (op)) {
-	unbounded_op_data_t ub;
-	ub.op = UNBOUNDED_MASK;
-	ub.u.mask.mask = img;
-	ub.u.mask.maskTransform = mask_matrix;
-	_cairo_quartz_fixup_unbounded_operation (surface, &ub, CAIRO_ANTIALIAS_NONE);
-    }
+    CGContextConcatCTM (state.context, mask_matrix);
+    CGContextClipToMaskPtr (state.context, rect, img);
+
+    CGContextSetCTM (state.context, ctm);
+
+    status = _cairo_quartz_surface_paint_internal (surface, &state);
+
+    _cairo_quartz_teardown_state (&state);
 
     CGImageRelease (img);
 
     return status;
 }
 
 /* This is somewhat less than ideal, but it gets the job done;
  * it would be better to avoid calling back into cairo.  This
--- a/gfx/thebes/GLContext.h
+++ b/gfx/thebes/GLContext.h
@@ -621,22 +621,22 @@ protected:
     GLuint mOffscreenStencilRB;
 
     // this should just be a std::bitset, but that ended up breaking
     // MacOS X builds; see bug 584919.  We can replace this with one
     // later on.
     template<size_t setlen>
     struct ExtensionBitset {
         ExtensionBitset() {
-            for (int i = 0; i < setlen; ++i)
+            for (size_t i = 0; i < setlen; ++i)
                 values[i] = false;
         }
 
-        bool& operator[](const int index) {
-            NS_ASSERTION(index >= 0 && index < setlen, "out of range");
+        bool& operator[](size_t index) {
+            NS_ASSERTION(index < setlen, "out of range");
             return values[index];
         }
 
         bool values[setlen];
     };
     ExtensionBitset<Extensions_Max> mAvailableExtensions;
 
     // Clear to transparent black, with 0 depth and stencil,
--- a/gfx/thebes/Makefile.in
+++ b/gfx/thebes/Makefile.in
@@ -14,16 +14,17 @@ EXPORT_LIBRARY	= 1
 EXPORTS	= \
 	gfx3DMatrix.h \
 	gfxASurface.h \
 	gfxAlphaRecovery.h \
 	gfxBlur.h \
 	gfxCachedTempSurface.h \
 	gfxColor.h \
 	gfxContext.h \
+	gfxDrawable.h \
 	gfxFont.h \
 	gfxFontConstants.h \
 	gfxFontUtils.h \
 	gfxFontTest.h \
 	gfxImageSurface.h \
 	gfxMatrix.h \
 	gfxPath.h \
 	gfxPattern.h \
@@ -161,16 +162,17 @@ endif
 endif
 
 CPPSRCS	= \
 	gfxASurface.cpp \
 	gfxAlphaRecovery.cpp \
 	gfxBlur.cpp \
 	gfxCachedTempSurface.cpp \
 	gfxContext.cpp \
+	gfxDrawable.cpp \
 	gfxImageSurface.cpp \
 	gfxFont.cpp \
 	gfxFontMissingGlyphs.cpp \
 	gfxFontTest.cpp \
 	gfxFontUtils.cpp \
 	gfxAtoms.cpp \
 	gfxMatrix.cpp \
 	gfxPath.cpp \
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxDrawable.cpp
@@ -0,0 +1,254 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Markus Stange.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "gfxDrawable.h"
+#include "gfxASurface.h"
+#include "gfxContext.h"
+#include "gfxPlatform.h"
+
+gfxSurfaceDrawable::gfxSurfaceDrawable(gfxASurface* aSurface,
+                                       const gfxIntSize aSize,
+                                       const gfxMatrix aTransform)
+ : gfxDrawable(aSize)
+ , mSurface(aSurface)
+ , mTransform(aTransform)
+{
+}
+
+static gfxMatrix
+DeviceToImageTransform(gfxContext* aContext,
+                       const gfxMatrix& aUserSpaceToImageSpace)
+{
+    gfxFloat deviceX, deviceY;
+    nsRefPtr<gfxASurface> currentTarget =
+        aContext->CurrentSurface(&deviceX, &deviceY);
+    gfxMatrix currentMatrix = aContext->CurrentMatrix();
+    gfxMatrix deviceToUser = gfxMatrix(currentMatrix).Invert();
+    deviceToUser.Translate(-gfxPoint(-deviceX, -deviceY));
+    return gfxMatrix(deviceToUser).Multiply(aUserSpaceToImageSpace);
+}
+
+static void 
+PreparePatternForUntiledDrawing(gfxPattern* aPattern,
+                                const gfxMatrix& aDeviceToImage,
+                                gfxASurface::gfxSurfaceType aSurfaceType,
+                                const gfxPattern::GraphicsFilter aDefaultFilter)
+{
+    // In theory we can handle this using cairo's EXTEND_PAD,
+    // but implementation limitations mean we have to consult
+    // the surface type.
+    switch (aSurfaceType) {
+        case gfxASurface::SurfaceTypeXlib:
+        case gfxASurface::SurfaceTypeXcb:
+        {
+            // See bug 324698.  This is a workaround for EXTEND_PAD not being
+            // implemented correctly on linux in the X server.
+            //
+            // Set the filter to CAIRO_FILTER_FAST --- otherwise,
+            // pixman's sampling will sample transparency for the outside edges
+            // and we'll get blurry edges.  CAIRO_EXTEND_PAD would also work
+            // here, if available
+            //
+            // But don't do this for simple downscales because it's horrible.
+            // Downscaling means that device-space coordinates are
+            // scaled *up* to find the image pixel coordinates.
+            PRBool isDownscale =
+                aDeviceToImage.xx >= 1.0 && aDeviceToImage.yy >= 1.0 &&
+                aDeviceToImage.xy == 0.0 && aDeviceToImage.yx == 0.0;
+            if (!isDownscale) {
+                aPattern->SetFilter(gfxPattern::FILTER_FAST);
+            }
+            break;
+        }
+
+        case gfxASurface::SurfaceTypeQuartz:
+        case gfxASurface::SurfaceTypeQuartzImage:
+            // Don't set EXTEND_PAD, Mac seems to be OK. Really?
+            aPattern->SetFilter(aDefaultFilter);
+            break;
+
+        default:
+            // turn on EXTEND_PAD.
+            // This is what we really want for all surface types, if the
+            // implementation was universally good.
+            aPattern->SetExtend(gfxPattern::EXTEND_PAD);
+            aPattern->SetFilter(aDefaultFilter);
+            break;
+    }
+}
+
+PRBool
+gfxSurfaceDrawable::Draw(gfxContext* aContext,
+                         const gfxRect& aFillRect,
+                         PRBool aRepeat,
+                         const gfxPattern::GraphicsFilter& aFilter,
+                         const gfxMatrix& aTransform)
+{
+    nsRefPtr<gfxPattern> pattern = new gfxPattern(mSurface);
+    if (aRepeat) {
+        pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
+        pattern->SetFilter(aFilter);
+    } else {
+        nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface();
+        gfxASurface::gfxSurfaceType surfaceType = currentTarget->GetType();
+        gfxMatrix deviceSpaceToImageSpace =
+            DeviceToImageTransform(aContext, aTransform);
+        PreparePatternForUntiledDrawing(pattern, deviceSpaceToImageSpace,
+                                        surfaceType, aFilter);
+    }
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+    pattern->SetFilter(gfxPattern::FILTER_FAST); 
+#endif
+    pattern->SetMatrix(gfxMatrix(aTransform).Multiply(mTransform));
+    aContext->NewPath();
+    aContext->SetPattern(pattern);
+    aContext->Rectangle(aFillRect);
+    aContext->Fill();
+    return PR_TRUE;
+}
+
+gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback,
+                                         const gfxIntSize aSize)
+ : gfxDrawable(aSize)
+ , mCallback(aCallback)
+{
+}
+
+already_AddRefed<gfxSurfaceDrawable>
+gfxCallbackDrawable::MakeSurfaceDrawable(const gfxPattern::GraphicsFilter aFilter)
+{
+    nsRefPtr<gfxASurface> surface =
+        gfxPlatform::GetPlatform()->CreateOffscreenSurface(mSize, gfxASurface::ImageFormatARGB32);
+    if (!surface || surface->CairoStatus() != 0)
+        return nsnull;
+
+    nsRefPtr<gfxContext> ctx = new gfxContext(surface);
+    Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), PR_FALSE, aFilter);
+    nsRefPtr<gfxSurfaceDrawable> drawable = new gfxSurfaceDrawable(surface, mSize);
+    return drawable.forget();
+}
+
+PRBool
+gfxCallbackDrawable::Draw(gfxContext* aContext,
+                          const gfxRect& aFillRect,
+                          PRBool aRepeat,
+                          const gfxPattern::GraphicsFilter& aFilter,
+                          const gfxMatrix& aTransform)
+{
+    if (aRepeat && !mSurfaceDrawable) {
+        mSurfaceDrawable = MakeSurfaceDrawable(aFilter);
+    }
+
+    if (mSurfaceDrawable)
+        return mSurfaceDrawable->Draw(aContext, aFillRect, aRepeat, aFilter,
+                                      aTransform);
+
+    if (mCallback)
+        return (*mCallback)(aContext, aFillRect, aFilter, aTransform);
+
+    return PR_FALSE;
+}
+
+gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern,
+                                       const gfxIntSize aSize)
+ : gfxDrawable(aSize)
+ , mPattern(aPattern)
+{
+}
+
+class DrawingCallbackFromDrawable : public gfxDrawingCallback {
+public:
+    DrawingCallbackFromDrawable(gfxDrawable* aDrawable)
+     : mDrawable(aDrawable) {
+        NS_ASSERTION(aDrawable, "aDrawable is null!");
+    }
+
+    virtual ~DrawingCallbackFromDrawable() {}
+
+    virtual PRBool operator()(gfxContext* aContext,
+                              const gfxRect& aFillRect,
+                              const gfxPattern::GraphicsFilter& aFilter,
+                              const gfxMatrix& aTransform = gfxMatrix())
+    {
+        return mDrawable->Draw(aContext, aFillRect, PR_FALSE, aFilter,
+                               aTransform);
+    }
+private:
+    nsRefPtr<gfxDrawable> mDrawable;
+};
+
+already_AddRefed<gfxCallbackDrawable>
+gfxPatternDrawable::MakeCallbackDrawable()
+{
+    nsRefPtr<gfxDrawingCallback> callback =
+        new DrawingCallbackFromDrawable(this);
+    nsRefPtr<gfxCallbackDrawable> callbackDrawable =
+        new gfxCallbackDrawable(callback, mSize);
+    return callbackDrawable.forget();
+}
+
+PRBool
+gfxPatternDrawable::Draw(gfxContext* aContext,
+                         const gfxRect& aFillRect,
+                         PRBool aRepeat,
+                         const gfxPattern::GraphicsFilter& aFilter,
+                         const gfxMatrix& aTransform)
+{
+    if (!mPattern)
+        return PR_FALSE;
+
+    if (aRepeat) {
+        // We can't use mPattern directly: We want our repeated tiles to have
+        // the size mSize, which might not be the case in mPattern.
+        // So we need to draw mPattern into a surface of size mSize, create
+        // a pattern from the surface and draw that pattern.
+        // gfxCallbackDrawable and gfxSurfaceDrawable already know how to do
+        // those things, so we use them here. Drawing mPattern into the surface
+        // will happen through this Draw() method with aRepeat = PR_FALSE.
+        nsRefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable();
+        return callbackDrawable->Draw(aContext, aFillRect, PR_TRUE, aFilter,
+                                      aTransform);
+    }
+
+    aContext->NewPath();
+    gfxMatrix oldMatrix = mPattern->GetMatrix();
+    mPattern->SetMatrix(gfxMatrix(aTransform).Multiply(oldMatrix));
+    aContext->SetPattern(mPattern);
+    aContext->Rectangle(aFillRect);
+    aContext->Fill();
+    mPattern->SetMatrix(oldMatrix);
+    return PR_TRUE;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxDrawable.h
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Markus Stange.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef GFX_DRAWABLE_H
+#define GFX_DRAWABLE_H
+
+#include "nsISupportsImpl.h"
+#include "nsAutoPtr.h"
+#include "gfxTypes.h"
+#include "gfxRect.h"
+#include "gfxColor.h"
+#include "gfxMatrix.h"
+#include "gfxPattern.h"
+
+class gfxASurface;
+class gfxContext;
+
+/**
+ * gfxDrawable
+ * An Interface representing something that has an intrinsic size and can draw
+ * itself repeatedly.
+ */
+class THEBES_API gfxDrawable {
+    NS_INLINE_DECL_REFCOUNTING(gfxDrawable)
+public:
+    gfxDrawable(const gfxIntSize aSize)
+     : mSize(aSize) {}
+    virtual ~gfxDrawable() {}
+
+    /**
+     * Draw into aContext filling aFillRect, possibly repeating, using aFilter.
+     * aTransform is a userspace to "image"space matrix. For example, if Draw
+     * draws using a gfxPattern, this is the matrix that should be set on the
+     * pattern prior to rendering it.
+     *  @return whether drawing was successful
+     */
+    virtual PRBool Draw(gfxContext* aContext,
+                        const gfxRect& aFillRect,
+                        PRBool aRepeat,
+                        const gfxPattern::GraphicsFilter& aFilter,
+                        const gfxMatrix& aTransform = gfxMatrix()) = 0;
+    virtual gfxIntSize Size() { return mSize; }
+
+protected:
+    const gfxIntSize mSize;
+};
+
+/**
+ * gfxSurfaceDrawable
+ * A convenience implementation of gfxDrawable for surfaces.
+ */
+class THEBES_API gfxSurfaceDrawable : public gfxDrawable {
+public:
+    gfxSurfaceDrawable(gfxASurface* aSurface, const gfxIntSize aSize,
+                       const gfxMatrix aTransform = gfxMatrix());
+    virtual ~gfxSurfaceDrawable() {}
+
+    virtual PRBool Draw(gfxContext* aContext,
+                        const gfxRect& aFillRect,
+                        PRBool aRepeat,
+                        const gfxPattern::GraphicsFilter& aFilter,
+                        const gfxMatrix& aTransform = gfxMatrix());
+
+protected:
+    nsRefPtr<gfxASurface> mSurface;
+    const gfxMatrix mTransform;
+};
+
+/**
+ * gfxDrawingCallback
+ * A simple drawing functor.
+ */
+class THEBES_API gfxDrawingCallback {
+    NS_INLINE_DECL_REFCOUNTING(gfxDrawingCallback)
+public:
+    virtual ~gfxDrawingCallback() {}
+
+    /**
+     * Draw into aContext filling aFillRect using aFilter.
+     * aTransform is a userspace to "image"space matrix. For example, if Draw
+     * draws using a gfxPattern, this is the matrix that should be set on the
+     * pattern prior to rendering it.
+     *  @return whether drawing was successful
+     */
+    virtual PRBool operator()(gfxContext* aContext,
+                              const gfxRect& aFillRect,
+                              const gfxPattern::GraphicsFilter& aFilter,
+                              const gfxMatrix& aTransform = gfxMatrix()) = 0;
+
+};
+
+/**
+ * gfxSurfaceDrawable
+ * A convenience implementation of gfxDrawable for callbacks.
+ */
+class THEBES_API gfxCallbackDrawable : public gfxDrawable {
+public:
+    gfxCallbackDrawable(gfxDrawingCallback* aCallback, const gfxIntSize aSize);
+    virtual ~gfxCallbackDrawable() {}
+
+    virtual PRBool Draw(gfxContext* aContext,
+                        const gfxRect& aFillRect,
+                        PRBool aRepeat,
+                        const gfxPattern::GraphicsFilter& aFilter,
+                        const gfxMatrix& aTransform = gfxMatrix());
+
+protected:
+    already_AddRefed<gfxSurfaceDrawable> MakeSurfaceDrawable(const gfxPattern::GraphicsFilter aFilter = gfxPattern::FILTER_FAST);
+
+    nsRefPtr<gfxDrawingCallback> mCallback;
+    nsRefPtr<gfxSurfaceDrawable> mSurfaceDrawable;
+};
+
+/**
+ * gfxPatternDrawable
+ * A convenience implementation of gfxDrawable for patterns.
+ */
+class THEBES_API gfxPatternDrawable : public gfxDrawable {
+public:
+    gfxPatternDrawable(gfxPattern* aPattern,
+                       const gfxIntSize aSize);
+    virtual ~gfxPatternDrawable() {}
+
+    virtual PRBool Draw(gfxContext* aContext,
+                        const gfxRect& aFillRect,
+                        PRBool aRepeat,
+                        const gfxPattern::GraphicsFilter& aFilter,
+                        const gfxMatrix& aTransform = gfxMatrix());
+
+protected:
+    already_AddRefed<gfxCallbackDrawable> MakeCallbackDrawable();
+
+    nsRefPtr<gfxPattern> mPattern;
+};
+
+#endif /* GFX_DRAWABLE_H */
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -30,18 +30,24 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "gfxImageSurface.h"
 #include "gfxUtils.h"
+#include "gfxContext.h"
+#include "gfxPlatform.h"
+#include "gfxDrawable.h"
+
+#if defined(XP_WIN) || defined(WINCE)
+#include "gfxWindowsPlatform.h"
+#endif
 
 static PRUint8 sUnpremultiplyTable[256*256];
 static PRUint8 sPremultiplyTable[256*256];
 static PRBool sTablesInitialized = PR_FALSE;
 
 static const PRUint8 PremultiplyValue(PRUint8 a, PRUint8 v) {
     return sPremultiplyTable[a*256+v];
 }
@@ -191,8 +197,224 @@ gfxUtils::UnpremultiplyImageSurface(gfxI
 
         *dst++ = a;
         *dst++ = UnpremultiplyValue(a, r);
         *dst++ = UnpremultiplyValue(a, g);
         *dst++ = UnpremultiplyValue(a, b);
 #endif
     }
 }
+
+static PRBool
+IsSafeImageTransformComponent(gfxFloat aValue)
+{
+  return aValue >= -32768 && aValue <= 32767;
+}
+
+// EXTEND_PAD won't help us here; we have to create a temporary surface to hold
+// the subimage of pixels we're allowed to sample.
+static already_AddRefed<gfxDrawable>
+CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
+                                 gfxContext* aContext,
+                                 const gfxMatrix& aUserSpaceToImageSpace,
+                                 const gfxRect& aSourceRect,
+                                 const gfxRect& aSubimage,
+                                 const gfxImageSurface::gfxImageFormat aFormat)
+{
+    gfxRect userSpaceClipExtents = aContext->GetClipExtents();
+    // This isn't optimal --- if aContext has a rotation then GetClipExtents
+    // will have to do a bounding-box computation, and TransformBounds might
+    // too, so we could get a better result if we computed image space clip
+    // extents in one go --- but it doesn't really matter and this is easier
+    // to understand.
+    gfxRect imageSpaceClipExtents =
+        aUserSpaceToImageSpace.TransformBounds(userSpaceClipExtents);
+    // Inflate by one pixel because bilinear filtering will sample at most
+    // one pixel beyond the computed image pixel coordinate.
+    imageSpaceClipExtents.Outset(1.0);
+
+    gfxRect needed = imageSpaceClipExtents.Intersect(aSourceRect);
+    needed = needed.Intersect(aSubimage);
+    needed.RoundOut();
+
+    // if 'needed' is empty, nothing will be drawn since aFill
+    // must be entirely outside the clip region, so it doesn't
+    // matter what we do here, but we should avoid trying to
+    // create a zero-size surface.
+    if (needed.IsEmpty())
+        return nsnull;
+
+    gfxIntSize size(PRInt32(needed.Width()), PRInt32(needed.Height()));
+    nsRefPtr<gfxASurface> temp =
+        gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, aFormat);
+    if (!temp || temp->CairoStatus())
+        return nsnull;
+
+    gfxContext tmpCtx(temp);
+    tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
+    aDrawable->Draw(&tmpCtx, needed - needed.pos, PR_TRUE,
+                    gfxPattern::FILTER_FAST, gfxMatrix().Translate(needed.pos));
+
+    nsRefPtr<gfxPattern> resultPattern = new gfxPattern(temp);
+    if (!resultPattern)
+        return nsnull;
+
+    nsRefPtr<gfxDrawable> drawable = 
+        new gfxSurfaceDrawable(temp, size, gfxMatrix().Translate(-needed.pos));
+    return drawable.forget();
+}
+
+// working around cairo/pixman bug (bug 364968)
+// Our device-space-to-image-space transform may not be acceptable to pixman.
+struct NS_STACK_CLASS AutoCairoPixmanBugWorkaround
+{
+    AutoCairoPixmanBugWorkaround(gfxContext*      aContext,
+                                 const gfxMatrix& aDeviceSpaceToImageSpace,
+                                 const gfxRect&   aFill,
+                                 const gfxASurface::gfxSurfaceType& aSurfaceType)
+     : mContext(aContext), mSucceeded(PR_TRUE), mPushedGroup(PR_FALSE)
+    {
+        // Quartz's limits for matrix are much larger than pixman
+        if (aSurfaceType == gfxASurface::SurfaceTypeQuartz)
+            return;
+
+        if (!IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.xx) ||
+            !IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.xy) ||
+            !IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.yx) ||
+            !IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.yy)) {
+            NS_WARNING("Scaling up too much, bailing out");
+            mSucceeded = PR_FALSE;
+            return;
+        }
+
+        if (IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.x0) &&
+            IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.y0))
+            return;
+
+        // We'll push a group, which will hopefully reduce our transform's
+        // translation so it's in bounds.
+        gfxMatrix currentMatrix = mContext->CurrentMatrix();
+        mContext->Save();
+
+        // Clip the rounded-out-to-device-pixels bounds of the
+        // transformed fill area. This is the area for the group we
+        // want to push.
+        mContext->IdentityMatrix();
+        gfxRect bounds = currentMatrix.TransformBounds(aFill);
+        bounds.RoundOut();
+        mContext->Clip(bounds);
+        mContext->SetMatrix(currentMatrix);
+        mContext->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
+        mContext->SetOperator(gfxContext::OPERATOR_OVER);
+
+        mPushedGroup = PR_TRUE;
+    }
+
+    ~AutoCairoPixmanBugWorkaround()
+    {
+        if (mPushedGroup) {
+            mContext->PopGroupToSource();
+            mContext->Paint();
+            mContext->Restore();
+        }
+    }
+
+    PRBool PushedGroup() { return mPushedGroup; }
+    PRBool Succeeded() { return mSucceeded; }
+
+private:
+    gfxContext* mContext;
+    PRPackedBool mSucceeded;
+    PRPackedBool mPushedGroup;
+};
+
+/**
+ * This returns the fastest operator to use for solid surfaces which have no
+ * alpha channel or their alpha channel is uniformly opaque.
+ * This differs per render mode.
+ */
+static gfxContext::GraphicsOperator
+OptimalFillOperator()
+{
+#ifdef XP_WIN
+    if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
+        gfxWindowsPlatform::RENDER_DIRECT2D) {
+        // D2D -really- hates operator source.
+        return gfxContext::OPERATOR_OVER;
+    } else {
+#endif
+        return gfxContext::OPERATOR_SOURCE;
+#ifdef XP_WIN
+    }
+#endif
+}
+
+static gfxMatrix
+DeviceToImageTransform(gfxContext* aContext,
+                       const gfxMatrix& aUserSpaceToImageSpace)
+{
+    gfxFloat deviceX, deviceY;
+    nsRefPtr<gfxASurface> currentTarget =
+        aContext->CurrentSurface(&deviceX, &deviceY);
+    gfxMatrix currentMatrix = aContext->CurrentMatrix();
+    gfxMatrix deviceToUser = gfxMatrix(currentMatrix).Invert();
+    deviceToUser.Translate(-gfxPoint(-deviceX, -deviceY));
+    return gfxMatrix(deviceToUser).Multiply(aUserSpaceToImageSpace);
+}
+
+/* static */ void
+gfxUtils::DrawPixelSnapped(gfxContext*      aContext,
+                           gfxDrawable*     aDrawable,
+                           const gfxMatrix& aUserSpaceToImageSpace,
+                           const gfxRect&   aSubimage,
+                           const gfxRect&   aSourceRect,
+                           const gfxRect&   aImageRect,
+                           const gfxRect&   aFill,
+                           const gfxImageSurface::gfxImageFormat aFormat,
+                           const gfxPattern::GraphicsFilter& aFilter)
+{
+    PRBool doTile = !aImageRect.Contains(aSourceRect);
+
+    nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface();
+    gfxASurface::gfxSurfaceType surfaceType = currentTarget->GetType();
+    gfxMatrix deviceSpaceToImageSpace =
+        DeviceToImageTransform(aContext, aUserSpaceToImageSpace);
+
+    AutoCairoPixmanBugWorkaround workaround(aContext, deviceSpaceToImageSpace,
+                                            aFill, surfaceType);
+    if (!workaround.Succeeded())
+        return;
+
+    nsRefPtr<gfxDrawable> drawable = aDrawable;
+
+    // OK now, the hard part left is to account for the subimage sampling
+    // restriction. If all the transforms involved are just integer
+    // translations, then we assume no resampling will occur so there's
+    // nothing to do.
+    // XXX if only we had source-clipping in cairo!
+    if (aContext->CurrentMatrix().HasNonIntegerTranslation() ||
+        aUserSpaceToImageSpace.HasNonIntegerTranslation()) {
+        if (doTile || !aSubimage.Contains(aImageRect)) {
+            nsRefPtr<gfxDrawable> restrictedDrawable =
+              CreateSamplingRestrictedDrawable(aDrawable, aContext,
+                                               aUserSpaceToImageSpace, aSourceRect,
+                                               aSubimage, aFormat);
+            if (restrictedDrawable) {
+                drawable.swap(restrictedDrawable);
+            }
+        }
+        // We no longer need to tile: Either we never needed to, or we already
+        // filled a surface with the tiled pattern; this surface can now be
+        // drawn without tiling.
+        doTile = PR_FALSE;
+    }
+
+    gfxContext::GraphicsOperator op = aContext->CurrentOperator();
+    if ((op == gfxContext::OPERATOR_OVER || workaround.PushedGroup()) &&
+        aFormat == gfxASurface::ImageFormatRGB24) {
+        aContext->SetOperator(OptimalFillOperator());
+    }
+
+    drawable->Draw(aContext, aFill, doTile, aFilter, aUserSpaceToImageSpace);
+
+    aContext->SetOperator(op);
+}
+
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -34,18 +34,20 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef GFX_UTILS_H
 #define GFX_UTILS_H
 
 #include "gfxTypes.h"
+#include "gfxPattern.h"
+#include "gfxImageSurface.h"
 
-class gfxImageSurface;
+class gfxDrawable;
 
 class THEBES_API gfxUtils {
 public:
     /*
      * Premultiply or Unpremultiply aSourceSurface, writing the result
      * to aDestSurface or back into aSourceSurface if aDestSurface is null.
      *
      * If aDestSurface is given, it must have identical format, dimensions, and
@@ -53,11 +55,34 @@ public:
      *
      * If the source is not ImageFormatARGB32, no operation is performed.  If
      * aDestSurface is given, the data is copied over.
      */
     static void PremultiplyImageSurface(gfxImageSurface *aSourceSurface,
                                         gfxImageSurface *aDestSurface = nsnull);
     static void UnpremultiplyImageSurface(gfxImageSurface *aSurface,
                                           gfxImageSurface *aDestSurface = nsnull);
+
+    /**
+     * Draw something drawable while working around limitations like bad support
+     * for EXTEND_PAD, lack of source-clipping, or cairo / pixman bugs with
+     * extreme user-space-to-image-space transforms.
+     *
+     * The input parameters here usually come from the output of our image
+     * snapping algorithm in nsLayoutUtils.cpp.
+     * This method is split from nsLayoutUtils::DrawPixelSnapped to allow for
+     * adjusting the parameters. For example, certain images with transparent
+     * margins only have a drawable subimage. For those images, imgFrame::Draw
+     * will tweak the rects and transforms that it gets from the pixel snapping
+     * algorithm before passing them on to this method.
+     */
+    static void DrawPixelSnapped(gfxContext*      aContext,
+                                 gfxDrawable*     aDrawable,
+                                 const gfxMatrix& aUserSpaceToImageSpace,
+                                 const gfxRect&   aSubimage,
+                                 const gfxRect&   aSourceRect,
+                                 const gfxRect&   aImageRect,
+                                 const gfxRect&   aFill,
+                                 const gfxImageSurface::gfxImageFormat aFormat,
+                                 const gfxPattern::GraphicsFilter& aFilter);
 };
 
 #endif
--- a/js/jsd/jsd_stak.c
+++ b/js/jsd/jsd_stak.c
@@ -143,18 +143,18 @@ jsd_NewThreadState(JSDContext* jsdc, JSC
                 (jsdthreadstate->stackDepth == 1 && frame &&
                  frame->jsdscript && !JSD_IS_DEBUG_ENABLED(jsdc, frame->jsdscript)))
             {
                 /*
                  * if we failed to create the first frame, or the top frame
                  * is not enabled for debugging, fail the entire thread state.
                  */
                 JS_INIT_CLIST(&jsdthreadstate->links);
+                JS_EndRequest(jsdthreadstate->context);
                 jsd_DestroyThreadState(jsdc, jsdthreadstate);
-                JS_EndRequest(jsdthreadstate->context);
                 return NULL;
             }
         }
     }
     JS_EndRequest(jsdthreadstate->context);
 
     if (jsdthreadstate->stackDepth == 0)
     {
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -1226,18 +1226,20 @@ jsdScript::GetParameterNames(PRUint32* c
     if (!cx) {
         NS_WARNING("No default context !?");
         return NS_ERROR_FAILURE;
     }
     JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
 
     JSAutoRequest ar(cx);
 
-    uintN nargs = JS_GetFunctionArgumentCount(cx, fun);
-    if (!fun || !JS_FunctionHasLocalNames(cx, fun) || nargs == 0) {
+    uintN nargs;
+    if (!fun ||
+        !JS_FunctionHasLocalNames(cx, fun) ||
+        (nargs = JS_GetFunctionArgumentCount(cx, fun)) == 0) {
         *count = 0;
         *paramNames = nsnull;
         return NS_OK;
     }
 
     PRUnichar **ret =
         static_cast<PRUnichar**>(NS_Alloc(nargs * sizeof(PRUnichar*)));
     if (!ret)
--- a/js/narcissus/jsdefs.js
+++ b/js/narcissus/jsdefs.js
@@ -40,20 +40,21 @@
  *
  * Well-known constants and lookup tables.  Many consts are generated from the
  * tokens table via eval to minimize redundancy, so consumers must be compiled
  * separately to take advantage of the simple switch-case constant propagation
  * done by SpiderMonkey.
  */
 
 Narcissus = {
-    options: { version: 185 }
+    options: { version: 185 },
+    hostGlobal: this
 };
 
-Narcissus.jsdefs = (function() {
+Narcissus.definitions = (function() {
 
     var tokens = [
         // End of source.
         "END",
 
         // Operators and punctuators.  Some pair-wise order matters, e.g. (+, -)
         // and (UNARY_PLUS, UNARY_MINUS).
         "\n", ";",
@@ -184,20 +185,92 @@ Narcissus.jsdefs = (function() {
     function defineGetter(obj, prop, fn, dontDelete, dontEnum) {
         Object.defineProperty(obj, prop, { get: fn, configurable: !dontDelete, enumerable: !dontEnum });
     }
 
     function defineProperty(obj, prop, val, dontDelete, readOnly, dontEnum) {
         Object.defineProperty(obj, prop, { value: val, writable: !readOnly, configurable: !dontDelete, enumerable: !dontEnum });
     }
 
+    // Returns true if fn is a native function.  (Note: SpiderMonkey specific.)
+    function isNativeCode(fn) {
+        // Relies on the toString method to identify native code.
+        return ((typeof fn) === "function") && fn.toString().match(/\[native code\]/);
+    }
+
+    function getPropertyDescriptor(obj, name) {
+        while (obj) {
+            if (({}).hasOwnProperty.call(obj, name))
+                return Object.getOwnPropertyDescriptor(obj, name);
+            obj = Object.getPrototypeOf(obj);
+        }
+    }
+
+    function getOwnProperties(obj) {
+        var map = {};
+        for (var name in Object.getOwnPropertyNames(obj))
+            map[name] = Object.getOwnPropertyDescriptor(obj, name);
+        return map;
+    }
+
+    function makePassthruHandler(obj) {
+        // Handler copied from
+        // http://wiki.ecmascript.org/doku.php?id=harmony:proxies&s=proxy%20object#examplea_no-op_forwarding_proxy
+        return {
+            getOwnPropertyDescriptor: function(name) {
+                var desc = Object.getOwnPropertyDescriptor(obj, name);
+
+                // a trapping proxy's properties must always be configurable
+                desc.configurable = true;
+                return desc;
+            },
+            getPropertyDescriptor: function(name) {
+                var desc = getPropertyDescriptor(obj, name);
+
+                // a trapping proxy's properties must always be configurable
+                desc.configurable = true;
+                return desc;
+            },
+            getOwnPropertyNames: function() {
+                return Object.getOwnPropertyNames(obj);
+            },
+            defineProperty: function(name, desc) {
+                Object.defineProperty(obj, name, desc);
+            },
+            delete: function(name) { return delete obj[name]; },
+            fix: function() {
+                if (Object.isFrozen(obj)) {
+                    return getOwnProperties(obj);
+                }
+
+                // As long as obj is not frozen, the proxy won't allow itself to be fixed.
+                return undefined; // will cause a TypeError to be thrown
+            },
+
+            has: function(name) { return name in obj; },
+            hasOwn: function(name) { return ({}).hasOwnProperty.call(obj, name); },
+            get: function(receiver, name) { return obj[name]; },
+
+            // bad behavior when set fails in non-strict mode
+            set: function(receiver, name, val) { obj[name] = val; return true; },
+            enumerate: function() {
+                var result = [];
+                for (name in obj) { result.push(name); };
+                return result;
+            },
+            keys: function() { return Object.keys(obj); }
+        };
+    }
+
     return {
-      "tokens": tokens,
-      "opTypeNames": opTypeNames,
-      "keywords": keywords,
-      "tokenIds": tokenIds,
-      "consts": consts,
-      "assignOps": assignOps,
-      "defineGetter": defineGetter,
-      "defineProperty": defineProperty
+        tokens: tokens,
+        opTypeNames: opTypeNames,
+        keywords: keywords,
+        tokenIds: tokenIds,
+        consts: consts,
+        assignOps: assignOps,
+        defineGetter: defineGetter,
+        defineProperty: defineProperty,
+        isNativeCode: isNativeCode,
+        makePassthruHandler: makePassthruHandler
     };
 }());
 
--- a/js/narcissus/jsexec.js
+++ b/js/narcissus/jsexec.js
@@ -43,102 +43,89 @@
  * Execution of parse trees.
  *
  * Standard classes except for eval, Function, Array, and String are borrowed
  * from the host JS environment.  Function is metacircular.  Array and String
  * are reflected via wrapping the corresponding native constructor and adding
  * an extra level of prototype-based delegation.
  */
 
-Narcissus.jsexec = (function() {
+Narcissus.interpreter = (function() {
 
-    var jsparse = Narcissus.jsparse;
-    var jsdefs = Narcissus.jsdefs;
+    var parser = Narcissus.parser;
+    var definitions = Narcissus.definitions;
+    var hostGlobal = Narcissus.hostGlobal;
 
     // Set constants in the local scope.
-    eval(jsdefs.consts);
+    eval(definitions.consts);
 
     const GLOBAL_CODE = 0, EVAL_CODE = 1, FUNCTION_CODE = 2;
 
     function ExecutionContext(type) {
         this.type = type;
     }
 
-    var global = {
+    function isStackOverflow(e) {
+        var re = /InternalError: (script stack space quota is exhausted|too much recursion)/;
+        return re.test(e.toString());
+    }
+
+    // The underlying global object for narcissus.
+    var narcissusGlobal = {
         // Value properties.
         NaN: NaN, Infinity: Infinity, undefined: undefined,
 
         // Function properties.
         eval: function eval(s) {
-            if (typeof s != "string")
+            if (typeof s !== "string")
                 return s;
 
             var x = ExecutionContext.current;
             var x2 = new ExecutionContext(EVAL_CODE);
             x2.thisObject = x.thisObject;
             x2.caller = x.caller;
             x2.callee = x.callee;
             x2.scope = x.scope;
-            ExecutionContext.current = x2;
             try {
-                execute(jsparse.parse(new jsparse.VanillaBuilder, s), x2);
-            } catch (e if e == THROW) {
-                x.result = x2.result;
-                throw e;
-            } catch (e if e instanceof SyntaxError) {
-                x.result = e;
-                throw THROW;
-            } catch (e if e instanceof InternalError) {
+                x2.execute(parser.parse(new parser.VanillaBuilder, s));
+                return x2.result;
+            } catch (e if e instanceof SyntaxError || isStackOverflow(e)) {
                 /*
-                 * If we get too much recursion during parsing we need to re-throw
-                 * it as a narcissus THROW.
+                 * If we get an internal error during parsing we need to reify
+                 * the exception as a Narcissus THROW.
                  *
                  * See bug 152646.
                  */
-                var re = /InternalError: (script stack space quota is exhausted|too much recursion)/;
-                if (re.test(e.toString())) {
-                    x.result = e;
-                    throw THROW;
-                } else {
-                    throw e;
-                }
-            } finally {
-                ExecutionContext.current = x;
+                x.result = e;
+                throw THROW;
             }
-            return x2.result;
         },
-        parseInt: parseInt, parseFloat: parseFloat,
-        isNaN: isNaN, isFinite: isFinite,
-        decodeURI: decodeURI, encodeURI: encodeURI,
-        decodeURIComponent: decodeURIComponent,
-        encodeURIComponent: encodeURIComponent,
 
-        // Class constructors.  Where ECMA-262 requires C.length == 1, we declare
+        // Class constructors.  Where ECMA-262 requires C.length === 1, we declare
         // a dummy formal parameter.
-        Object: Object,
         Function: function Function(dummy) {
             var p = "", b = "", n = arguments.length;
             if (n) {
                 var m = n - 1;
                 if (m) {
                     p += arguments[0];
                     for (var k = 1; k < m; k++)
                         p += "," + arguments[k];
                 }
                 b += arguments[m];
             }
 
             // XXX We want to pass a good file and line to the tokenizer.
             // Note the anonymous name to maintain parity with Spidermonkey.
-            var t = new jsparse.Tokenizer("anonymous(" + p + ") {" + b + "}");
+            var t = new parser.Tokenizer("anonymous(" + p + ") {" + b + "}");
 
             // NB: Use the STATEMENT_FORM constant since we don't want to push this
             // function onto the fake compilation context.
-            var x = { builder: new jsparse.VanillaBuilder };
-            var f = jsparse.FunctionDefinition(t, x, false, jsparse.STATEMENT_FORM);
+            var x = { builder: new parser.VanillaBuilder };
+            var f = parser.FunctionDefinition(t, x, false, parser.STATEMENT_FORM);
             var s = {object: global, parent: null};
             return newFunction(f,{scope:s});
         },
         Array: function (dummy) {
             // Array when called as a function acts as a constructor.
             return Array.apply(this, arguments);
         },
         String: function String(s) {
@@ -147,47 +134,84 @@ Narcissus.jsexec = (function() {
             if (this instanceof String) {
                 // Called as constructor: save the argument as the string value
                 // of this String object and return this object.
                 this.value = s;
                 return this;
             }
             return s;
         },
-        Boolean: Boolean, Number: Number, Date: Date, RegExp: RegExp,
-        Error: Error, EvalError: EvalError, RangeError: RangeError,
-        ReferenceError: ReferenceError, SyntaxError: SyntaxError,
-        TypeError: TypeError, URIError: URIError,
 
-        // Other properties.
-        Math: Math,
+        //Don't want to proxy RegExp or some features won't work
+        RegExp: RegExp,
 
         // Extensions to ECMA.
-        snarf: snarf, evaluate: evaluate,
         load: function load(s) {
-            if (typeof s != "string")
+            if (typeof s !== "string")
                 return s;
 
             evaluate(snarf(s), s, 1)
         },
-        print: print,
         version: function() { return Narcissus.options.version; },
         quit: function() { throw END; }
     };
 
+    // Create global handler with needed modifications.
+    var globalHandler = definitions.makePassthruHandler(narcissusGlobal);
+    globalHandler.has = function(name) {
+        if (name in narcissusGlobal) { return true; }
+        // Hide Narcissus implementation code.
+        else if (name === "Narcissus") { return false; }
+        else { return (name in hostGlobal); }
+    };
+    globalHandler.get = function(receiver, name) {
+        if (narcissusGlobal.hasOwnProperty(name)) {
+            return narcissusGlobal[name];
+        }
+        var globalFun = hostGlobal[name];
+        if (definitions.isNativeCode(globalFun)) {
+            // Enables native browser functions like 'alert' to work correctly.
+            return Proxy.createFunction(
+                    definitions.makePassthruHandler(globalFun),
+                    function() { return globalFun.apply(hostGlobal, arguments); },
+                    function() {
+                        var a = arguments;
+                        switch (a.length) {
+                          case 0:
+                            return new globalFun();
+                          case 1:
+                            return new globalFun(a[0]);
+                          case 2:
+                            return new globalFun(a[0], a[1]);
+                          case 3:
+                            return new globalFun(a[0], a[1], a[2]);
+                          default:
+                            var argStr = "";
+                            for (var i=0; i<a.length; i++) {
+                                argStr += 'a[' + i + '],';
+                            }
+                            return eval('new ' + name + '(' + argStr.slice(0,-1) + ');');
+                        }
+                    });
+        }
+        else { return globalFun; };
+    };
+
+    var global = Proxy.create(globalHandler);
+
     // Helper to avoid Object.prototype.hasOwnProperty polluting scope objects.
     function hasDirectProperty(o, p) {
         return Object.prototype.hasOwnProperty.call(o, p);
     }
 
     // Reflect a host class into the target global environment by delegation.
     function reflectClass(name, proto) {
         var gctor = global[name];
-        jsdefs.defineProperty(gctor, "prototype", proto, true, true, true);
-        jsdefs.defineProperty(proto, "constructor", gctor, false, false, true);
+        definitions.defineProperty(gctor, "prototype", proto, true, true, true);
+        definitions.defineProperty(proto, "constructor", gctor, false, false, true);
         return proto;
     }
 
     // Reflect Array -- note that all Array methods are generic.
     reflectClass('Array', new Array);
 
     // Reflect String, overriding non-generic methods.
     var gSp = reflectClass('String', new String);
@@ -201,28 +225,29 @@ Narcissus.jsexec = (function() {
     ExecutionContext.prototype = {
         caller: null,
         callee: null,
         scope: {object: global, parent: null},
         thisObject: global,
         result: undefined,
         target: null,
         ecma3OnlyMode: false,
-        // Run a thunk in this execution context and return its result.
-        run: function(thunk) {
+        // Execute a node in this execution context.
+        execute: function(n) {
             var prev = ExecutionContext.current;
             ExecutionContext.current = this;
             try {
-                thunk();
-                return this.result;
-            } catch (e if e == THROW) {
+                execute(n, this);
+            } catch (e if e === THROW) {
+                // Propagate the throw to the previous context if it exists.
                 if (prev) {
                     prev.result = this.result;
                     throw THROW;
                 }
+                // Otherwise reflect the throw into host JS.
                 throw this.result;
             } finally {
                 ExecutionContext.current = prev;
             }
         }
     };
 
     function Reference(base, propertyName, node) {
@@ -248,25 +273,25 @@ Narcissus.jsexec = (function() {
         if (v instanceof Reference)
             return (v.base || global)[v.propertyName] = w;
         throw new ReferenceError("Invalid assignment left-hand side",
                                  vn.filename, vn.lineno);
     }
 
     function isPrimitive(v) {
         var t = typeof v;
-        return (t == "object") ? v === null : t != "function";
+        return (t === "object") ? v === null : t !== "function";
     }
 
     function isObject(v) {
         var t = typeof v;
-        return (t == "object") ? v !== null : t == "function";
+        return (t === "object") ? v !== null : t === "function";
     }
 
-    // If r instanceof Reference, v == getValue(r); else v === r.  If passed, rn
+    // If r instanceof Reference, v === getValue(r); else v === r.  If passed, rn
     // is the node whose execute result was r.
     function toObject(v, r, rn) {
         switch (typeof v) {
           case "boolean":
             return new global.Boolean(v);
           case "number":
             return new global.Number(v);
           case "string":
@@ -282,52 +307,52 @@ Narcissus.jsexec = (function() {
                  : new TypeError(message);
     }
 
     function execute(n, x) {
         var a, f, i, j, r, s, t, u, v;
 
         switch (n.type) {
           case FUNCTION:
-            if (n.functionForm != jsparse.DECLARED_FORM) {
-                if (!n.name || n.functionForm == jsparse.STATEMENT_FORM) {
+            if (n.functionForm !== parser.DECLARED_FORM) {
+                if (!n.name || n.functionForm === parser.STATEMENT_FORM) {
                     v = newFunction(n, x);
-                    if (n.functionForm == jsparse.STATEMENT_FORM)
-                        jsdefs.defineProperty(x.scope.object, n.name, v, true);
+                    if (n.functionForm === parser.STATEMENT_FORM)
+                        definitions.defineProperty(x.scope.object, n.name, v, true);
                 } else {
                     t = new Object;
                     x.scope = {object: t, parent: x.scope};
                     try {
                         v = newFunction(n, x);
-                        jsdefs.defineProperty(t, n.name, v, true, true);
+                        definitions.defineProperty(t, n.name, v, true, true);
                     } finally {
                         x.scope = x.scope.parent;
                     }
                 }
             }
             break;
 
           case SCRIPT:
             t = x.scope.object;
             a = n.funDecls;
             for (i = 0, j = a.length; i < j; i++) {
                 s = a[i].name;
                 f = newFunction(a[i], x);
-                jsdefs.defineProperty(t, s, f, x.type != EVAL_CODE);
+                definitions.defineProperty(t, s, f, x.type !== EVAL_CODE);
             }
             a = n.varDecls;
             for (i = 0, j = a.length; i < j; i++) {
                 u = a[i];
                 s = u.name;
                 if (u.readOnly && hasDirectProperty(t, s)) {
                     throw new TypeError("Redeclaration of const " + s,
                                         u.filename, u.lineno);
                 }
                 if (u.readOnly || !hasDirectProperty(t, s)) {
-                    jsdefs.defineProperty(t, s, undefined, x.type != EVAL_CODE, u.readOnly);
+                    definitions.defineProperty(t, s, undefined, x.type !== EVAL_CODE, u.readOnly);
                 }
             }
             // FALL THROUGH
 
           case BLOCK:
             for (i = 0, j = n.length; i < j; i++)
                 execute(n[i], x);
             break;
@@ -340,122 +365,124 @@ Narcissus.jsexec = (function() {
             break;
 
           case SWITCH:
             s = getValue(execute(n.discriminant, x));
             a = n.cases;
             var matchDefault = false;
           switch_loop:
             for (i = 0, j = a.length; ; i++) {
-                if (i == j) {
+                if (i === j) {
                     if (n.defaultIndex >= 0) {
                         i = n.defaultIndex - 1; // no case matched, do default
                         matchDefault = true;
                         continue;
                     }
                     break;                      // no default, exit switch_loop
                 }
                 t = a[i];                       // next case (might be default!)
-                if (t.type == CASE) {
+                if (t.type === CASE) {
                     u = getValue(execute(t.caseLabel, x));
                 } else {
                     if (!matchDefault)          // not defaulting, skip for now
                         continue;
                     u = s;                      // force match to do default
                 }
                 if (u === s) {
                     for (;;) {                  // this loop exits switch_loop
                         if (t.statements.length) {
                             try {
                                 execute(t.statements, x);
-                            } catch (e if e == BREAK && x.target == n) {
+                            } catch (e if e === BREAK && x.target === n) {
                                 break switch_loop;
                             }
                         }
-                        if (++i == j)
+                        if (++i === j)
                             break switch_loop;
                         t = a[i];
                     }
                     // NOT REACHED
                 }
             }
             break;
 
           case FOR:
             n.setup && getValue(execute(n.setup, x));
             // FALL THROUGH
           case WHILE:
             while (!n.condition || getValue(execute(n.condition, x))) {
                 try {
                     execute(n.body, x);
-                } catch (e if e == BREAK && x.target == n) {
+                } catch (e if e === BREAK && x.target === n) {
                     break;
-                } catch (e if e == CONTINUE && x.target == n) {
+                } catch (e if e === CONTINUE && x.target === n) {
                     // Must run the update expression.
                 }
                 n.update && getValue(execute(n.update, x));
             }
             break;
 
           case FOR_IN:
             u = n.varDecl;
             if (u)
                 execute(u, x);
             r = n.iterator;
             s = execute(n.object, x);
             v = getValue(s);
 
             // ECMA deviation to track extant browser JS implementation behavior.
-            t = (v == null && !x.ecma3OnlyMode) ? v : toObject(v, s, n.object);
+            t = ((v === null || v === undefined) && !x.ecma3OnlyMode)
+              ? v
+              : toObject(v, s, n.object);
             a = [];
             for (i in t)
                 a.push(i);
             for (i = 0, j = a.length; i < j; i++) {
                 putValue(execute(r, x), a[i], r);
                 try {
                     execute(n.body, x);
-                } catch (e if e == BREAK && x.target == n) {
+                } catch (e if e === BREAK && x.target === n) {
                     break;
-                } catch (e if e == CONTINUE && x.target == n) {
+                } catch (e if e === CONTINUE && x.target === n) {
                     continue;
                 }
             }
             break;
 
           case DO:
             do {
                 try {
                     execute(n.body, x);
-                } catch (e if e == BREAK && x.target == n) {
+                } catch (e if e === BREAK && x.target === n) {
                     break;
-                } catch (e if e == CONTINUE && x.target == n) {
+                } catch (e if e === CONTINUE && x.target === n) {
                     continue;
                 }
             } while (getValue(execute(n.condition, x)));
             break;
 
           case BREAK:
           case CONTINUE:
             x.target = n.target;
             throw n.type;
 
           case TRY:
             try {
                 execute(n.tryBlock, x);
-            } catch (e if e == THROW && (j = n.catchClauses.length)) {
+            } catch (e if e === THROW && (j = n.catchClauses.length)) {
                 e = x.result;
                 x.result = undefined;
                 for (i = 0; ; i++) {
-                    if (i == j) {
+                    if (i === j) {
                         x.result = e;
                         throw THROW;
                     }
                     t = n.catchClauses[i];
                     x.scope = {object: {}, parent: x.scope};
-                    jsdefs.defineProperty(x.scope.object, t.varName, e, true);
+                    definitions.defineProperty(x.scope.object, t.varName, e, true);
                     try {
                         if (t.guard && !getValue(execute(t.guard, x)))
                             continue;
                         execute(t.block, x);
                         break;
                     } finally {
                         x.scope = x.scope.parent;
                     }
@@ -492,35 +519,35 @@ Narcissus.jsexec = (function() {
                 if (!u)
                     continue;
                 t = n[i].name;
                 for (s = x.scope; s; s = s.parent) {
                     if (hasDirectProperty(s.object, t))
                         break;
                 }
                 u = getValue(execute(u, x));
-                if (n.type == CONST)
-                    jsdefs.defineProperty(s.object, t, u, x.type != EVAL_CODE, true);
+                if (n.type === CONST)
+                    definitions.defineProperty(s.object, t, u, x.type !== EVAL_CODE, true);
                 else
                     s.object[t] = u;
             }
             break;
 
           case DEBUGGER:
-            throw "NYI: " + jsdefs.tokens[n.type];
+            throw "NYI: " + definitions.tokens[n.type];
 
           case SEMICOLON:
             if (n.expression)
                 x.result = getValue(execute(n.expression, x));
             break;
 
           case LABEL:
             try {
                 execute(n.statement, x);
-            } catch (e if e == BREAK && x.target == n) {
+            } catch (e if e === BREAK && x.target === n) {
             }
             break;
 
           case COMMA:
             for (i = 0, j = n.length; i < j; i++)
                 v = getValue(execute(n[i], x));
             break;
 
@@ -607,17 +634,17 @@ Narcissus.jsexec = (function() {
 
           case IN:
             v = getValue(execute(n[0], x)) in getValue(execute(n[1], x));
             break;
 
           case INSTANCEOF:
             t = getValue(execute(n[0], x));
             u = getValue(execute(n[1], x));
-            if (isObject(u) && typeof u.__hasInstance__ == "function")
+            if (isObject(u) && typeof u.__hasInstance__ === "function")
                 v = u.__hasInstance__(t);
             else
                 v = t instanceof u;
             break;
 
           case LSH:
             v = getValue(execute(n[0], x)) << getValue(execute(n[1], x));
             break;
@@ -683,17 +710,17 @@ Narcissus.jsexec = (function() {
             break;
 
           case INCREMENT:
           case DECREMENT:
             t = execute(n[0], x);
             u = Number(getValue(t));
             if (n.postfix)
                 v = u;
-            putValue(t, (n.type == INCREMENT) ? ++u : --u, n[0]);
+            putValue(t, (n.type === INCREMENT) ? ++u : --u, n[0]);
             if (!n.postfix)
                 v = u;
             break;
 
           case DOT:
             r = execute(n[0], x);
             t = getValue(r);
             u = n[1].value;
@@ -707,46 +734,46 @@ Narcissus.jsexec = (function() {
             v = new Reference(toObject(t, r, n[0]), String(u), n);
             break;
 
           case LIST:
             // Curse ECMA for specifying that arguments is not an Array object!
             v = {};
             for (i = 0, j = n.length; i < j; i++) {
                 u = getValue(execute(n[i], x));
-                jsdefs.defineProperty(v, i, u, false, false, true);
+                definitions.defineProperty(v, i, u, false, false, true);
             }
-            jsdefs.defineProperty(v, "length", i, false, false, true);
+            definitions.defineProperty(v, "length", i, false, false, true);
             break;
 
           case CALL:
             r = execute(n[0], x);
             a = execute(n[1], x);
             f = getValue(r);
-            if (isPrimitive(f) || typeof f.__call__ != "function") {
+            if (isPrimitive(f) || typeof f.__call__ !== "function") {
                 throw new TypeError(r + " is not callable",
                                     n[0].filename, n[0].lineno);
             }
             t = (r instanceof Reference) ? r.base : null;
             if (t instanceof Activation)
                 t = null;
             v = f.__call__(t, a, x);
             break;
 
           case NEW:
           case NEW_WITH_ARGS:
             r = execute(n[0], x);
             f = getValue(r);
-            if (n.type == NEW) {
+            if (n.type === NEW) {
                 a = {};
-                jsdefs.defineProperty(a, "length", 0, false, false, true);
+                definitions.defineProperty(a, "length", 0, false, false, true);
             } else {
                 a = execute(n[1], x);
             }
-            if (isPrimitive(f) || typeof f.__construct__ != "function") {
+            if (isPrimitive(f) || typeof f.__construct__ !== "function") {
                 throw new TypeError(r + " is not a constructor",
                                     n[0].filename, n[0].lineno);
             }
             v = f.__construct__(a, x);
             break;
 
           case ARRAY_INIT:
             v = [];
@@ -756,22 +783,22 @@ Narcissus.jsexec = (function() {
             }
             v.length = j;
             break;
 
           case OBJECT_INIT:
             v = {};
             for (i = 0, j = n.length; i < j; i++) {
                 t = n[i];
-                if (t.type == PROPERTY_INIT) {
+                if (t.type === PROPERTY_INIT) {
                     v[t[0].value] = getValue(execute(t[1], x));
                 } else {
                     f = newFunction(t, x);
-                    u = (t.type == GETTER) ? '__defineGetter__'
-                                           : '__defineSetter__';
+                    u = (t.type === GETTER) ? '__defineGetter__'
+                                            : '__defineSetter__';
                     v[u](t.name, thunk(f, x));
                 }
             }
             break;
 
           case NULL:
             v = null;
             break;
@@ -810,129 +837,62 @@ Narcissus.jsexec = (function() {
             throw "PANIC: unknown operation " + n.type + ": " + uneval(n);
         }
 
         return v;
     }
 
     function Activation(f, a) {
         for (var i = 0, j = f.params.length; i < j; i++)
-            jsdefs.defineProperty(this, f.params[i], a[i], true);
-        jsdefs.defineProperty(this, "arguments", a, true);
+            definitions.defineProperty(this, f.params[i], a[i], true);
+        definitions.defineProperty(this, "arguments", a, true);
     }
 
     // Null Activation.prototype's proto slot so that Object.prototype.* does not
     // pollute the scope of heavyweight functions.  Also delete its 'constructor'
     // property so that it doesn't pollute function scopes.
 
     Activation.prototype.__proto__ = null;
     delete Activation.prototype.constructor;
 
     function FunctionObject(node, scope) {
         this.node = node;
         this.scope = scope;
-        jsdefs.defineProperty(this, "length", node.params.length, true, true, true);
+        definitions.defineProperty(this, "length", node.params.length, true, true, true);
         var proto = {};
-        jsdefs.defineProperty(this, "prototype", proto, true);
-        jsdefs.defineProperty(proto, "constructor", this, false, false, true);
-    }
-
-    function getPropertyDescriptor(obj, name) {
-        while (obj) {
-            if (({}).hasOwnProperty.call(obj, name))
-                return Object.getOwnPropertyDescriptor(obj, name);
-            obj = Object.getPrototypeOf(obj);
-        }
-    }
-
-    function getOwnProperties(obj) {
-        var map = {};
-        for (var name in Object.getOwnPropertyNames(obj))
-            map[name] = Object.getOwnPropertyDescriptor(obj, name);
-        return map;
+        definitions.defineProperty(this, "prototype", proto, true);
+        definitions.defineProperty(proto, "constructor", this, false, false, true);
     }
 
     // Returns a new function wrapped with a Proxy.
     function newFunction(n, x) {
         var fobj = new FunctionObject(n, x.scope);
-
-        // Handler copied from
-        // http://wiki.ecmascript.org/doku.php?id=harmony:proxies&s=proxy%20object#examplea_no-op_forwarding_proxy
-        var handler = {
-            getOwnPropertyDescriptor: function(name) {
-                var desc = Object.getOwnPropertyDescriptor(fobj, name);
-
-                // a trapping proxy's properties must always be configurable
-                desc.configurable = true;
-                return desc;
-            },
-            getPropertyDescriptor: function(name) {
-                var desc = getPropertyDescriptor(fobj, name);
-
-                // a trapping proxy's properties must always be configurable
-                desc.configurable = true;
-                return desc;
-            },
-            getOwnPropertyNames: function() {
-                return Object.getOwnPropertyNames(fobj);
-            },
-            defineProperty: function(name, desc) {
-                Object.defineProperty(fobj, name, desc);
-            },
-            delete: function(name) { return delete fobj[name]; },
-            fix: function() {
-                if (Object.isFrozen(fobj)) {
-                    return getOwnProperties(fobj);
-                }
-
-                // As long as fobj is not frozen, the proxy won't allow itself to be fixed.
-                return undefined; // will cause a TypeError to be thrown
-            },
-
-            has: function(name) { return name in fobj; },
-            hasOwn: function(name) { return ({}).hasOwnProperty.call(fobj, name); },
-            get: function(receiver, name) { return fobj[name]; },
-
-            // bad behavior when set fails in non-strict mode
-            set: function(receiver, name, val) { fobj[name] = val; return true; },
-            enumerate: function() {
-                var result = [];
-                for (name in fobj) { result.push(name); };
-                return result;
-            },
-            keys: function() { return Object.keys(fobj); }
-        };
+        var handler = definitions.makePassthruHandler(fobj);
         var p = Proxy.createFunction(handler,
                                      function() { return fobj.__call__(this, arguments, x); },
                                      function() { return fobj.__construct__(arguments, x); });
         return p;
     }
 
     var FOp = FunctionObject.prototype = {
 
         // Internal methods.
         __call__: function (t, a, x) {
             var x2 = new ExecutionContext(FUNCTION_CODE);
             x2.thisObject = t || global;
             x2.caller = x;
             x2.callee = this;
-            jsdefs.defineProperty(a, "callee", this, false, false, true);
+            definitions.defineProperty(a, "callee", this, false, false, true);
             var f = this.node;
             x2.scope = {object: new Activation(f, a), parent: this.scope};
 
-            ExecutionContext.current = x2;
             try {
-                execute(f.body, x2);
-            } catch (e if e == RETURN) {
+                x2.execute(f.body);
+            } catch (e if e === RETURN) {
                 return x2.result;
-            } catch (e if e == THROW) {
-                x.result = x2.result;
-                throw THROW;
-            } finally {
-                ExecutionContext.current = x;
             }
             return undefined;
         },
 
         __construct__: function (a, x) {
             var o = new Object;
             var p = this.prototype;
             if (isObject(p))
@@ -950,48 +910,48 @@ Narcissus.jsexec = (function() {
                 return false;
             var p = this.prototype;
             if (isPrimitive(p)) {
                 throw new TypeError("'prototype' property is not an object",
                                     this.node.filename, this.node.lineno);
             }
             var o;
             while ((o = v.__proto__)) {
-                if (o == p)
+                if (o === p)
                     return true;
                 v = o;
             }
             return false;
         },
 
         // Standard methods.
         toString: function () {
             return this.node.getSource();
         },
 
         apply: function (t, a) {
             // Curse ECMA again!
-            if (typeof this.__call__ != "function") {
+            if (typeof this.__call__ !== "function") {
                 throw new TypeError("Function.prototype.apply called on" +
                                     " uncallable object");
             }
 
             if (t === undefined || t === null)
                 t = global;
-            else if (typeof t != "object")
+            else if (typeof t !== "object")
                 t = toObject(t, t);
 
             if (a === undefined || a === null) {
                 a = {};
-                jsdefs.defineProperty(a, "length", 0, false, false, true);
+                definitions.defineProperty(a, "length", 0, false, false, true);
             } else if (a instanceof Array) {
                 var v = {};
                 for (var i = 0, j = a.length; i < j; i++)
-                    jsdefs.defineProperty(v, i, a[i], false, false, true);
-                jsdefs.defineProperty(v, "length", i, false, false, true);
+                    definitions.defineProperty(v, i, a[i], false, false, true);
+                definitions.defineProperty(v, "length", i, false, false, true);
                 a = v;
             } else if (!(a instanceof Object)) {
                 // XXX check for a non-arguments object
                 throw new TypeError("Second argument to Function.prototype.apply" +
                                     " must be an array or arguments object",
                                     this.node.filename, this.node.lineno);
             }
 
@@ -1008,28 +968,28 @@ Narcissus.jsexec = (function() {
     // Connect Function.prototype and Function.prototype.constructor in global.
     reflectClass('Function', FOp);
 
     // Help native and host-scripted functions be like FunctionObjects.
     var Fp = Function.prototype;
     var REp = RegExp.prototype;
 
     if (!('__call__' in Fp)) {
-        jsdefs.defineProperty(Fp, "__call__",
+        definitions.defineProperty(Fp, "__call__",
                        function (t, a, x) {
                            // Curse ECMA yet again!
                            a = Array.prototype.splice.call(a, 0, a.length);
                            return this.apply(t, a);
                        }, true, true, true);
-        jsdefs.defineProperty(REp, "__call__",
+        definitions.defineProperty(REp, "__call__",
                        function (t, a, x) {
                            a = Array.prototype.splice.call(a, 0, a.length);
                            return this.exec.apply(this, a);
                        }, true, true, true);
-        jsdefs.defineProperty(Fp, "__construct__",
+        definitions.defineProperty(Fp, "__construct__",
                        function (a, x) {
                            a = Array.prototype.splice.call(a, 0, a.length);
                            switch (a.length) {
                              case 0:
                                return new this();
                              case 1:
                                return new this(a[0]);
                              case 2:
@@ -1043,103 +1003,97 @@ Narcissus.jsexec = (function() {
                                }
                                return eval('new this(' + argStr.slice(0,-1) + ');');
                            }
                        }, true, true, true);
 
         // Since we use native functions such as Date along with host ones such
         // as global.eval, we want both to be considered instances of the native
         // Function constructor.
-        jsdefs.defineProperty(Fp, "__hasInstance__",
+        definitions.defineProperty(Fp, "__hasInstance__",
                        function (v) {
                            return v instanceof Function || v instanceof global.Function;
                        }, true, true, true);
     }
 
     function thunk(f, x) {
         return function () { return f.__call__(this, arguments, x); };
     }
 
     function evaluate(s, f, l) {
-        if (typeof s != "string")
+        if (typeof s !== "string")
             return s;
 
-        var x = ExecutionContext.current;
-        var x2 = new ExecutionContext(GLOBAL_CODE);
-        ExecutionContext.current = x2;
-        try {
-            execute(jsparse.parse(new jsparse.VanillaBuilder, s, f, l), x2);
-        } catch (e if e == THROW) {
-            if (x) {
-                x.result = x2.result;
-                throw THROW;
-            }
-            throw x2.result;
-        } finally {
-            ExecutionContext.current = x;
-        }
-        return x2.result;
+        var x = new ExecutionContext(GLOBAL_CODE);
+        x.execute(parser.parse(new parser.VanillaBuilder, s, f, l));
+        return x.result;
     }
 
     // A read-eval-print-loop that roughly tracks the behavior of the js shell.
     function repl() {
 
         // Display a value similarly to the js shell.
         function display(x) {
-            if (typeof x == "object") {
+            if (typeof x === "object") {
                 // At the js shell, objects with no |toSource| don't print.
-                if (x != null && "toSource" in x) {
+                if (x !== null && "toSource" in x) {
                     try {
                         print(x.toSource());
                     } catch (e) {
                     }
                 } else {
                     print("null");
                 }
-            } else if (typeof x == "string") {
+            } else if (typeof x === "string") {
                 print(uneval(x));
-            } else if (typeof x != "undefined") {
+            } else if (typeof x !== "undefined") {
                 // Since x must be primitive, String can't throw.
                 print(String(x));
             }
         }
 
         // String conversion that never throws.
         function string(x) {
             try {
                 return String(x);
             } catch (e) {
                 return "unknown (can't convert to string)";
             }
         }
 
-        var b = new jsparse.VanillaBuilder;
+        var b = new parser.VanillaBuilder;
         var x = new ExecutionContext(GLOBAL_CODE);
 
-        x.run(function() {
-            for (;;) {
-                putstr("njs> ");
-                var line = readline();
-                x.result = undefined;
-                try {
-                    execute(jsparse.parse(b, line, "stdin", 1), x);
-                    display(x.result);
-                } catch (e if e == THROW) {
-                    print("uncaught exception: " + string(x.result));
-                } catch (e if e == END) {
-                    break;
-                } catch (e if e instanceof SyntaxError) {
-                    print(e.toString());
-                } catch (e) {
-                    print("internal Narcissus error");
-                    throw e;
-                }
+        ExecutionContext.current = x;
+        for (;;) {
+            x.result = undefined;
+            putstr("njs> ");
+            var line = readline();
+            // If readline receives EOF it returns null.
+            if (line === null) {
+                print("");
+                break;
             }
-        });
+            try {
+                execute(parser.parse(b, line, "stdin", 1), x);
+                display(x.result);
+            } catch (e if e === THROW) {
+                print("uncaught exception: " + string(x.result));
+            } catch (e if e === END) {
+                break;
+            } catch (e if e instanceof SyntaxError) {
+                print(e.toString());
+            } catch (e) {
+                print("internal Narcissus error");
+                throw e;
+            }
+        }
+        ExecutionContext.current = null;
     }
 
     return {
-        "evaluate": evaluate,
-        "repl": repl
+        global: global,
+        evaluate: evaluate,
+        repl: repl
     };
 
 }());
 
--- a/js/narcissus/jslex.js
+++ b/js/narcissus/jslex.js
@@ -36,26 +36,26 @@
  * ***** END LICENSE BLOCK ***** */
 
 /*
  * Narcissus - JS implemented in JS.
  *
  * Lexical scanner.
  */
 
-Narcissus.jslex = (function() {
+Narcissus.lexer = (function() {
 
-    var jsdefs = Narcissus.jsdefs;
+    var definitions = Narcissus.definitions;
 
     // Set constants in the local scope.
-    eval(jsdefs.consts);
+    eval(definitions.consts);
 
     // Build up a trie of operator tokens.
     var opTokens = {};
-    for (var op in jsdefs.opTypeNames) {
+    for (var op in definitions.opTypeNames) {
         if (op === '\n' || op === '.')
             continue;
 
         var node = opTokens;
         for (var i = 0; i < op.length; i++) {
             var ch = op[i];
             if (!(ch in node))
                 node[ch] = {};
@@ -77,38 +77,38 @@ Narcissus.jslex = (function() {
         this.filename = f || "";
         this.lineno = l || 1;
     }
 
     Tokenizer.prototype = {
         get done() {
             // We need to set scanOperand to true here because the first thing
             // might be a regexp.
-            return this.peek(true) == END;
+            return this.peek(true) === END;
         },
 
         get token() {
             return this.tokens[this.tokenIndex];
         },
 
         match: function (tt, scanOperand) {
-            return this.get(scanOperand) == tt || this.unget();
+            return this.get(scanOperand) === tt || this.unget();
         },
 
         mustMatch: function (tt) {
             if (!this.match(tt))
                 throw this.newSyntaxError("Missing " + tokens[tt].toLowerCase());
             return this.token;
         },
 
         peek: function (scanOperand) {
             var tt, next;
             if (this.lookahead) {
                 next = this.tokens[(this.tokenIndex + this.lookahead) & 3];
-                tt = (this.scanNewlines && next.lineno != this.lineno)
+                tt = (this.scanNewlines && next.lineno !== this.lineno)
                      ? NEWLINE
                      : next.type;
             } else {
                 tt = this.get(scanOperand);
                 this.unget();
             }
             return tt;
         },
@@ -332,23 +332,23 @@ Narcissus.jslex = (function() {
                 if (next in node) {
                     node = node[next];
                     this.cursor++;
                     next = input[this.cursor];
                 }
             }
 
             var op = node.op;
-            if (jsdefs.assignOps[op] && input[this.cursor] === '=') {
+            if (definitions.assignOps[op] && input[this.cursor] === '=') {
                 this.cursor++;
                 token.type = ASSIGN;
-                token.assignOp = jsdefs.tokenIds[jsdefs.opTypeNames[op]];
+                token.assignOp = definitions.tokenIds[definitions.opTypeNames[op]];
                 op += '=';
             } else {
-                token.type = jsdefs.tokenIds[jsdefs.opTypeNames[op]];
+                token.type = definitions.tokenIds[definitions.opTypeNames[op]];
                 token.assignOp = null;
             }
 
             token.value = op;
         },
 
         // FIXME: Unicode escape sequences
         // FIXME: Unicode identifiers
@@ -358,33 +358,33 @@ Narcissus.jslex = (function() {
             do {
                 ch = input[this.cursor++];
             } while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
                      (ch >= '0' && ch <= '9') || ch === '$' || ch === '_');
 
             this.cursor--;  // Put the non-word character back.
 
             var id = input.substring(token.start, this.cursor);
-            token.type = jsdefs.keywords[id] || IDENTIFIER;
+            token.type = definitions.keywords[id] || IDENTIFIER;
             token.value = id;
         },
 
         /*
          * Tokenizer.get :: void -> token type
          *
          * Consumes input *only* if there is no lookahead.
          * Dispatch to the appropriate lexing function depending on the input.
          */
         get: function (scanOperand) {
             var token;
             while (this.lookahead) {
                 --this.lookahead;
                 this.tokenIndex = (this.tokenIndex + 1) & 3;
                 token = this.tokens[this.tokenIndex];
-                if (token.type != NEWLINE || this.scanNewlines)
+                if (token.type !== NEWLINE || this.scanNewlines)
                     return token.type;
             }
 
             this.skip();
 
             this.tokenIndex = (this.tokenIndex + 1) & 3;
             token = this.tokens[this.tokenIndex];
             if (!token)
@@ -426,17 +426,17 @@ Narcissus.jslex = (function() {
         },
 
         /*
          * Tokenizer.unget :: void -> undefined
          *
          * Match depends on unget returning undefined.
          */
         unget: function () {
-            if (++this.lookahead == 4) throw "PANIC: too much lookahead!";
+            if (++this.lookahead === 4) throw "PANIC: too much lookahead!";
             this.tokenIndex = (this.tokenIndex - 1) & 3;
         },
 
         newSyntaxError: function (m) {
             var e = new SyntaxError(m, this.filename, this.lineno);
             e.source = this.source;
             e.cursor = this.cursor;
             return e;
@@ -458,12 +458,12 @@ Narcissus.jslex = (function() {
             this.tokenIndex = point.tokenIndex;
             this.tokens = point.tokens.slice();
             this.lookahead = point.lookahead;
             this.scanNewline = point.scanNewline;
             this.lineno = point.lineno;
         }
     };
 
-    return { "Tokenizer": Tokenizer };
+    return { Tokenizer: Tokenizer };
 
 }());
 
--- a/js/narcissus/jsparse.js
+++ b/js/narcissus/jsparse.js
@@ -38,29 +38,28 @@
  * ***** END LICENSE BLOCK ***** */
 
 /*
  * Narcissus - JS implemented in JS.
  *
  * Parser.
  */
 
-Narcissus.jsparse = (function() {
+Narcissus.parser = (function() {
 
-    var jslex = Narcissus.jslex;
-    var jsdefs = Narcissus.jsdefs;
+    var lexer = Narcissus.lexer;
+    var definitions = Narcissus.definitions;
 
     // Set constants in the local scope.
-    eval(jsdefs.consts);
+    eval(definitions.consts);
 
    /*
     * The vanilla AST builder.
     */
-
-    VanillaBuilder = function VanillaBuilder() {
+    function VanillaBuilder() {
     }
 
     VanillaBuilder.prototype = {
         IF$build: function(t) {
             return new Node(t, IF);
         },
 
         IF$setCondition: function(n, e) {
@@ -378,18 +377,18 @@ Narcissus.jsparse = (function() {
             n.statement = s;
         },
 
         LABEL$finish: function(n) {
         },
 
         FUNCTION$build: function(t) {
             var n = new Node(t);
-            if (n.type != FUNCTION)
-                n.type = (n.value == "get") ? GETTER : SETTER;
+            if (n.type !== FUNCTION)
+                n.type = (n.value === "get") ? GETTER : SETTER;
             n.params = [];
             return n;
         },
 
         FUNCTION$setName: function(n, v) {
             n.name = v;
         },
 
@@ -655,19 +654,19 @@ Narcissus.jsparse = (function() {
         },
 
         MULTIPLY$finish: function(n) {
         },
 
         UNARY$build: function(t) {
             // NB t.token.type must be DELETE, VOID, TYPEOF, NOT, BITWISE_NOT,
             // UNARY_PLUS, UNARY_MINUS, INCREMENT, or DECREMENT.
-            if (t.token.type == PLUS)
+            if (t.token.type === PLUS)
                 t.token.type = UNARY_PLUS;
-            else if (t.token.type == MINUS)
+            else if (t.token.type === MINUS)
                 t.token.type = UNARY_MINUS;
             return new Node(t);
         },
 
         UNARY$addOperand: function(n, n2) {
             n.push(n2);
         },
 
@@ -822,17 +821,17 @@ Narcissus.jsparse = (function() {
         var n = Statements(t, x);
         n.type = SCRIPT;
         n.funDecls = x.funDecls;
         n.varDecls = x.varDecls;
         return n;
     }
 
     // Node extends Array, which we extend slightly with a top-of-stack method.
-    jsdefs.defineProperty(Array.prototype, "top",
+    definitions.defineProperty(Array.prototype, "top",
                    function() {
                        return this.length && this[this.length-1];
                    }, false, false, true);
 
     /*
      * Node :: (tokenizer, optional type) -> node
      */
     function Node(t, type) {
@@ -869,24 +868,24 @@ Narcissus.jsparse = (function() {
                 this.end = kid.end;
         }
         return Array.prototype.push.call(this, kid);
     }
 
     Node.indentLevel = 0;
 
     function tokenstr(tt) {
-        var t = jsdefs.tokens[tt];
-        return /^\W/.test(t) ? jsdefs.opTypeNames[t] : t.toUpperCase();
+        var t = definitions.tokens[tt];
+        return /^\W/.test(t) ? definitions.opTypeNames[t] : t.toUpperCase();
     }
 
     Np.toString = function () {
         var a = [];
         for (var i in this) {
-            if (this.hasOwnProperty(i) && i != 'type' && i != 'target')
+            if (this.hasOwnProperty(i) && i !== 'type' && i !== 'target')
                 a.push({id: i, value: this[i]});
         }
         a.sort(function (a,b) { return (a.id < b.id) ? -1 : 1; });
         const INDENTATION = "    ";
         var n = ++Node.indentLevel;
         var s = "{\n" + INDENTATION.repeat(n) + "type: " + tokenstr(this.type);
         for (i = 0; i < a.length; i++)
             s += ",\n" + INDENTATION.repeat(n) + a[i].id + ": " + a[i].value;
@@ -894,22 +893,22 @@ Narcissus.jsparse = (function() {
         s += "\n" + INDENTATION.repeat(n) + "}";
         return s;
     }
 
     Np.getSource = function () {
         return this.tokenizer.source.slice(this.start, this.end);
     };
 
-    jsdefs.defineGetter(Np, "filename",
+    definitions.defineGetter(Np, "filename",
                  function() {
                      return this.tokenizer.filename;
                  });
 
-    jsdefs.defineProperty(String.prototype, "repeat",
+    definitions.defineProperty(String.prototype, "repeat",
                    function(n) {
                        var s = "", t = this + s;
                        while (--n >= 0)
                            s += t;
                        return s;
                    }, false, false, true);
 
     // Statement stack and nested statement handler.
@@ -922,27 +921,40 @@ Narcissus.jsparse = (function() {
     }
 
     /*
      * Statements :: (tokenizer, compiler context) -> node
      *
      * Parses a list of Statements.
      */
     function Statements(t, x) {
+        /*
+         * Blocks are uniquely numbered by a blockId within a function that is
+         * at the top level of the program. blockId starts from 0.
+         *
+         * This is done to aid hoisting for parse-time analyses done in custom
+         * builders.
+         *
+         * For more details in its interaction with hoisting, see comments in
+         * FunctionDefinition.
+         */
         var b = x.builder;
         var n = b.BLOCK$build(t, x.blockId++);
         b.BLOCK$hoistLets(n);
         x.stmtStack.push(n);
-        while (!t.done && t.peek(true) != RIGHT_CURLY)
+        while (!t.done && t.peek(true) !== RIGHT_CURLY)
             b.BLOCK$addStatement(n, Statement(t, x));
         x.stmtStack.pop();
         b.BLOCK$finish(n);
         if (n.needsHoisting) {
             b.setHoists(n.id, n.varDecls);
-            // Propagate up to the function.
+            /*
+             * If a block needs hoisting, we need to propagate this flag up to
+             * the CompilerContext.
+             */
             x.needsHoisting = true;
         }
         return n;
     }
 
     function Block(t, x) {
         t.mustMatch(LEFT_CURLY);
         var n = Statements(t, x);
@@ -988,64 +1000,64 @@ Narcissus.jsparse = (function() {
             return n;
 
           case SWITCH:
             // This allows CASEs after a DEFAULT, which is in the standard.
             n = b.SWITCH$build(t);
             b.SWITCH$setDiscriminant(n, ParenExpression(t, x));
             x.stmtStack.push(n);
             t.mustMatch(LEFT_CURLY);
-            while ((tt = t.get()) != RIGHT_CURLY) {
+            while ((tt = t.get()) !== RIGHT_CURLY) {
                 switch (tt) {
                   case DEFAULT:
                     if (n.defaultIndex >= 0)
                         throw t.newSyntaxError("More than one switch default");
                     n2 = b.DEFAULT$build(t);
                     b.SWITCH$setDefaultIndex(n, n.cases.length);
                     t.mustMatch(COLON);
                     b.DEFAULT$initializeStatements(n2, t);
-                    while ((tt=t.peek(true)) != CASE && tt != DEFAULT &&
-                           tt != RIGHT_CURLY)
+                    while ((tt=t.peek(true)) !== CASE && tt !== DEFAULT &&
+                           tt !== RIGHT_CURLY)
                         b.DEFAULT$addStatement(n2, Statement(t, x));
                     b.DEFAULT$finish(n2);
                     break;
 
                   case CASE:
                     n2 = b.CASE$build(t);
                     b.CASE$setLabel(n2, Expression(t, x, COLON));
                     t.mustMatch(COLON);
                     b.CASE$initializeStatements(n2, t);
-                    while ((tt=t.peek(true)) != CASE && tt != DEFAULT &&
-                           tt != RIGHT_CURLY)
+                    while ((tt=t.peek(true)) !== CASE && tt !== DEFAULT &&
+                           tt !== RIGHT_CURLY)
                         b.CASE$addStatement(n2, Statement(t, x));
                     b.CASE$finish(n2);
                     break;
 
                   default:
                     throw t.newSyntaxError("Invalid switch case");
                 }
                 b.SWITCH$addCase(n, n2);
             }
             x.stmtStack.pop();
             b.SWITCH$finish(n);
             return n;
 
           case FOR:
             n = b.FOR$build(t);
-            if (t.match(IDENTIFIER) && t.token.value == "each")
+            if (t.match(IDENTIFIER) && t.token.value === "each")
                 b.FOR$rebuildForEach(n);
             t.mustMatch(LEFT_PAREN);
-            if ((tt = t.peek()) != SEMICOLON) {
+            if ((tt = t.peek()) !== SEMICOLON) {
                 x.inForLoopInit = true;
-                if (tt == VAR || tt == CONST) {
+                if (tt === VAR || tt === CONST) {
                     t.get();
                     n2 = Variables(t, x);
-                } else if (tt == LET) {
+                } else if (tt === LET) {
                     t.get();
-                    if (t.peek() == LEFT_PAREN) {
+                    if (t.peek() === LEFT_PAREN) {
                         n2 = LetBlock(t, x, false);
                     } else {
                         /*
                          * Let in for head, we need to add an implicit block
                          * around the rest of the for.
                          */
                         var forBlock = b.BLOCK$build(t, x.blockId++);
                         x.stmtStack.push(forBlock);
@@ -1054,35 +1066,35 @@ Narcissus.jsparse = (function() {
                 } else {
                     n2 = Expression(t, x);
                 }
                 x.inForLoopInit = false;
             }
             if (n2 && t.match(IN)) {
                 b.FOR$rebuildForIn(n);
                 b.FOR$setObject(n, Expression(t, x), forBlock);
-                if (n2.type == VAR || n2.type == LET) {
-                    if (n2.length != 1) {
+                if (n2.type === VAR || n2.type === LET) {
+                    if (n2.length !== 1) {
                         throw new SyntaxError("Invalid for..in left-hand side",
                                               t.filename, n2.lineno);
                     }
                     b.FOR$setIterator(n, n2[0], n2, forBlock);
                 } else {
                     b.FOR$setIterator(n, n2, null, forBlock);
                 }
             } else {
                 b.FOR$setSetup(n, n2);
                 t.mustMatch(SEMICOLON);
                 if (n.isEach)
                     throw t.newSyntaxError("Invalid for each..in loop");
-                b.FOR$setCondition(n, (t.peek() == SEMICOLON)
+                b.FOR$setCondition(n, (t.peek() === SEMICOLON)
                                   ? null
                                   : Expression(t, x));
                 t.mustMatch(SEMICOLON);
-                b.FOR$setUpdate(n, (t.peek() == RIGHT_PAREN)
+                b.FOR$setUpdate(n, (t.peek() === RIGHT_PAREN)
                                    ? null
                                    : Expression(t, x));
             }
             t.mustMatch(RIGHT_PAREN);
             b.FOR$setBody(n, nest(t, x, n, Statement));
             if (forBlock) {
                 b.BLOCK$finish(forBlock);
                 x.stmtStack.pop();
@@ -1108,59 +1120,59 @@ Narcissus.jsparse = (function() {
                 // See http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
                 t.match(SEMICOLON);
                 return n;
             }
             break;
 
           case BREAK:
           case CONTINUE:
-            n = tt == BREAK ? b.BREAK$build(t) : b.CONTINUE$build(t);
+            n = tt === BREAK ? b.BREAK$build(t) : b.CONTINUE$build(t);
 
-            if (t.peekOnSameLine() == IDENTIFIER) {
+            if (t.peekOnSameLine() === IDENTIFIER) {
                 t.get();
-                if (tt == BREAK)
+                if (tt === BREAK)
                     b.BREAK$setLabel(n, t.token.value);
                 else
                     b.CONTINUE$setLabel(n, t.token.value);
             }
 
             ss = x.stmtStack;
             i = ss.length;
             label = n.label;
 
             if (label) {
                 do {
                     if (--i < 0)
                         throw t.newSyntaxError("Label not found");
-                } while (ss[i].label != label);
+                } while (ss[i].label !== label);
 
                 /*
                  * Both break and continue to label need to be handled specially
                  * within a labeled loop, so that they target that loop. If not in
                  * a loop, then break targets its labeled statement. Labels can be
                  * nested so we skip all labels immediately enclosing the nearest
                  * non-label statement.
                  */
-                while (i < ss.length - 1 && ss[i+1].type == LABEL)
+                while (i < ss.length - 1 && ss[i+1].type === LABEL)
                     i++;
                 if (i < ss.length - 1 && ss[i+1].isLoop)
                     i++;
-                else if (tt == CONTINUE)
+                else if (tt === CONTINUE)
                     throw t.newSyntaxError("Invalid continue");
             } else {
                 do {
                     if (--i < 0) {
-                        throw t.newSyntaxError("Invalid " + ((tt == BREAK)
+                        throw t.newSyntaxError("Invalid " + ((tt === BREAK)
                                                              ? "break"
                                                              : "continue"));
                     }
-                } while (!ss[i].isLoop && !(tt == BREAK && ss[i].type == SWITCH));
+                } while (!ss[i].isLoop && !(tt === BREAK && ss[i].type === SWITCH));
             }
-            if (tt == BREAK) {
+            if (tt === BREAK) {
                 b.BREAK$setTarget(n, ss[i]);
                 b.BREAK$finish(n);
             } else {
                 b.CONTINUE$setTarget(n, ss[i]);
                 b.CONTINUE$finish(n);
             }
             break;
 
@@ -1202,17 +1214,17 @@ Narcissus.jsparse = (function() {
                 b.TRY$setFinallyBlock(n, Block(t, x));
             if (!n.catchClauses.length && !n.finallyBlock)
                 throw t.newSyntaxError("Invalid try statement");
             b.TRY$finish(n);
             return n;
 
           case CATCH:
           case FINALLY:
-            throw t.newSyntaxError(jsdefs.tokens[tt] + " without preceding try");
+            throw t.newSyntaxError(definitions.tokens[tt] + " without preceding try");
 
           case THROW:
             n = b.THROW$build(t);
             b.THROW$setException(n, Expression(t, x));
             b.THROW$finish(n);
             break;
 
           case RETURN:
@@ -1227,17 +1239,17 @@ Narcissus.jsparse = (function() {
             return n;
 
           case VAR:
           case CONST:
             n = Variables(t, x);
             break;
 
           case LET:
-            if (t.peek() == LEFT_PAREN)
+            if (t.peek() === LEFT_PAREN)
                 n = LetBlock(t, x, true);
             else
                 n = Variables(t, x);
             break;
 
           case DEBUGGER:
             n = b.DEBUGGER$build(t);
             break;
@@ -1245,24 +1257,24 @@ Narcissus.jsparse = (function() {
           case NEWLINE:
           case SEMICOLON:
             n = b.SEMICOLON$build(t);
             b.SEMICOLON$setExpression(n, null);
             b.SEMICOLON$finish(t);
             return n;
 
           default:
-            if (tt == IDENTIFIER) {
+            if (tt === IDENTIFIER) {
                 tt = t.peek();
                 // Labeled statement.
-                if (tt == COLON) {
+                if (tt === COLON) {
                     label = t.token.value;
                     ss = x.stmtStack;
                     for (i = ss.length-1; i >= 0; --i) {
-                        if (ss[i].label == label)
+                        if (ss[i].label === label)
                             throw t.newSyntaxError("Duplicate label");
                     }
                     t.get();
                     n = b.LABEL$build(t);
                     b.LABEL$setLabel(n, label)
                     b.LABEL$setStatement(n, nest(t, x, n, Statement));
                     b.LABEL$finish(n);
                     return n;
@@ -1280,58 +1292,58 @@ Narcissus.jsparse = (function() {
         }
 
         MagicalSemicolon(t);
         return n;
     }
 
     function MagicalSemicolon(t) {
         var tt;
-        if (t.lineno == t.token.lineno) {
+        if (t.lineno === t.token.lineno) {
             tt = t.peekOnSameLine();
-            if (tt != END && tt != NEWLINE && tt != SEMICOLON && tt != RIGHT_CURLY)
+            if (tt !== END && tt !== NEWLINE && tt !== SEMICOLON && tt !== RIGHT_CURLY)
                 throw t.newSyntaxError("missing ; before statement");
         }
         t.match(SEMICOLON);
     }
 
     function returnOrYield(t, x) {
         var n, b = x.builder, tt = t.token.type, tt2;
 
-        if (tt == RETURN) {
+        if (tt === RETURN) {
             if (!x.inFunction)
                 throw t.newSyntaxError("Return not in function");
             n = b.RETURN$build(t);
-        } else /* (tt == YIELD) */ {
+        } else /* (tt === YIELD) */ {
             if (!x.inFunction)
                 throw t.newSyntaxError("Yield not in function");
             x.isGenerator = true;
             n = b.YIELD$build(t);
         }
 
         tt2 = t.peek(true);
-        if (tt2 != END && tt2 != NEWLINE && tt2 != SEMICOLON && tt2 != RIGHT_CURLY
-            && (tt != YIELD ||
-                (tt2 != tt && tt2 != RIGHT_BRACKET && tt2 != RIGHT_PAREN &&
-                 tt2 != COLON && tt2 != COMMA))) {
-            if (tt == RETURN) {
+        if (tt2 !== END && tt2 !== NEWLINE && tt2 !== SEMICOLON && tt2 !== RIGHT_CURLY
+            && (tt !== YIELD ||
+                (tt2 !== tt && tt2 !== RIGHT_BRACKET && tt2 !== RIGHT_PAREN &&
+                 tt2 !== COLON && tt2 !== COMMA))) {
+            if (tt === RETURN) {
                 b.RETURN$setValue(n, Expression(t, x));
                 x.hasReturnWithValue = true;
             } else {
                 b.YIELD$setValue(n, AssignExpression(t, x));
             }
-        } else if (tt == RETURN) {
+        } else if (tt === RETURN) {
             x.hasEmptyReturn = true;
         }
 
         // Disallow return v; in generator.
         if (x.hasReturnWithValue && x.isGenerator)
             throw t.newSyntaxError("Generator returns a value");
 
-        if (tt == RETURN)
+        if (tt === RETURN)
             b.RETURN$finish(n);
         else
             b.YIELD$finish(n);
 
         return n;
     }
 
     /*
@@ -1365,91 +1377,122 @@ Narcissus.jsparse = (function() {
                     break;
                 }
             } while (t.match(COMMA));
             t.mustMatch(RIGHT_PAREN);
         }
 
         // Do we have an expression closure or a normal body?
         var tt = t.get();
-        if (tt != LEFT_CURLY)
+        if (tt !== LEFT_CURLY)
             t.unget();
 
         var x2 = new StaticContext(true, b);
         var rp = t.save();
         if (x.inFunction) {
             /*
-             * Inner functions don't reset block numbering. They also need to
-             * remember which block they were parsed in for hoisting (see comment
-             * below).
+             * Inner functions don't reset block numbering, only functions at
+             * the top level of the program do.
              */
             x2.blockId = x.blockId;
         }
 
-        if (tt != LEFT_CURLY) {
+        if (tt !== LEFT_CURLY) {
             b.FUNCTION$setBody(f, AssignExpression(t, x));
             if (x.isGenerator)
                 throw t.newSyntaxError("Generator returns a value");
         } else {
             b.FUNCTION$hoistVars(x2.blockId);
             b.FUNCTION$setBody(f, Script(t, x2));
         }
 
         /*
-         * To linearize hoisting with nested blocks needing hoists, if a toplevel
-         * function has any hoists we reparse the entire thing. Each toplevel
-         * function is parsed at most twice.
+         * Hoisting makes parse-time binding analysis tricky. A taxonomy of hoists:
+         *
+         * 1. vars hoist to the top of their function:
          *
-         * Pass 1: If there needs to be hoisting at any child block or inner
-         * function, the entire function gets reparsed.
+         *    var x = 'global';
+         *    function f() {
+         *      x = 'f';
+         *      if (false)
+         *        var x;
+         *    }
+         *    f();
+         *    print(x); // "global"
          *
-         * Pass 2: It's possible that hoisting has changed the upvars of
-         * functions. That is, consider:
+         * 2. lets hoist to the top of their block:
          *
-         * function f() {
-         *   x = 0;
-         *   g();
-         *   x; // x's forward pointer should be invalidated!
-         *   function g() {
-         *     x = 'g';
-         *   }
-         *   var x;
-         * }
+         *    function f() { // id: 0
+         *      var x = 'f';
+         *      {
+         *        {
+         *          print(x); // "undefined"
+         *        }
+         *        let x;
+         *      }
+         *    }
+         *    f();
+         *
+         * 3. inner functions at function top-level hoist to the beginning
+         *    of the function.
          *
-         * So, a function needs to remember in which block it is parsed under
-         * (since the function body is _not_ hoisted, only the declaration) and
-         * upon hoisting, needs to recalculate all its upvars up front.
+         * If the builder used is doing parse-time analyses, hoisting may
+         * invalidate earlier conclusions it makes about variable scope.
+         *
+         * The builder can opt to set the needsHoisting flag in a
+         * CompilerContext (in the case of var and function hoisting) or in a
+         * node of type BLOCK (in the case of let hoisting). This signals for
+         * the parser to reparse sections of code.
+         *
+         * To avoid exponential blowup, if a function at the program top-level
+         * has any hoists in its child blocks or inner functions, we reparse
+         * the entire toplevel function. Each toplevel function is parsed at
+         * most twice.
+         *
+         * The list of declarations can be tied to block ids to aid talking
+         * about declarations of blocks that have not yet been fully parsed.
+         *
+         * Blocks are already uniquely numbered; see the comment in
+         * Statements.
          */
         if (x2.needsHoisting) {
-            // Order is important here! funDecls must come _after_ varDecls!
+
+            /*
+             * Order is important here! Builders expect funDecls to come after
+             * varDecls!
+             */
             b.setHoists(f.body.id, x2.varDecls.concat(x2.funDecls));
 
             if (x.inFunction) {
-                // Propagate up to the parent function if we're an inner function.
+                /*
+                 * If an inner function needs hoisting, we need to propagate
+                 * this flag up to the parent function.
+                 */
                 x.needsHoisting = true;
             } else {
-                // Only re-parse toplevel functions.
-                var x3 = x2;
+                // Only re-parse functions at the top level of the program.
                 x2 = new StaticContext(true, b);
                 t.rewind(rp);
-                // Set a flag in case the builder wants to have different behavior
-                // on the second pass.
+                /*
+                 * Set a flag in case the builder wants to have different behavior
+                 * on the second pass.
+                 */
                 b.secondPass = true;
                 b.FUNCTION$hoistVars(f.body.id, true);
                 b.FUNCTION$setBody(f, Script(t, x2));
                 b.secondPass = false;
             }
         }
 
-        if (tt == LEFT_CURLY)
+        if (tt === LEFT_CURLY)
             t.mustMatch(RIGHT_CURLY);
 
         f.end = t.token.end;
         f.functionForm = functionForm;
-        if (functionForm == DECLARED_FORM)
+        if (functionForm === DECLARED_FORM)
             x.funDecls.push(f);
         b.FUNCTION$finish(f, x);
         return f;
     }
 
     /*
      * Variables :: (tokenizer, compiler context) -> node
      *
@@ -1481,17 +1524,17 @@ Narcissus.jsparse = (function() {
             if (!letBlock) {
                 ss = x.stmtStack;
                 i = ss.length;
                 while (ss[--i].type !== BLOCK) ; // a BLOCK *must* be found.
                 /*
                  * Lets at the function toplevel are just vars, at least in
                  * SpiderMonkey.
                  */
-                if (i == 0) {
+                if (i === 0) {
                     build = b.VAR$build;
                     addDecl = b.VAR$addDecl;
                     finish = b.VAR$finish;
                     s = x;
                 } else {
                     s = ss[i];
                 }
             } else {
@@ -1504,24 +1547,24 @@ Narcissus.jsparse = (function() {
         do {
             var tt = t.get();
             /*
              * FIXME Should have a special DECLARATION node instead of overloading
              * IDENTIFIER to mean both identifier declarations and destructured
              * declarations.
              */
             var n2 = b.DECL$build(t);
-            if (tt == LEFT_BRACKET || tt == LEFT_CURLY) {
+            if (tt === LEFT_BRACKET || tt === LEFT_CURLY) {
                 // Pass in s if we need to add each pattern matched into
                 // its varDecls, else pass in x.
                 var data = null;
                 // Need to unget to parse the full destructured expression.
                 t.unget();
                 b.DECL$setName(n2, DestructuringExpression(t, x, true, s));
-                if (x.inForLoopInit && t.peek() == IN) {
+                if (x.inForLoopInit && t.peek() === IN) {
                     addDecl.call(b, n, n2, s);
                     continue;
                 }
 
                 t.mustMatch(ASSIGN);
                 if (t.token.assignOp)
                     throw t.newSyntaxError("Invalid variable initialization");
 
@@ -1533,21 +1576,21 @@ Narcissus.jsparse = (function() {
 
                 // But only add the rhs as the initializer.
                 b.DECL$setInitializer(n2, n3[1]);
                 b.DECL$finish(n2);
                 addDecl.call(b, n, n2, s);
                 continue;
             }
 
-            if (tt != IDENTIFIER)
+            if (tt !== IDENTIFIER)
                 throw t.newSyntaxError("missing variable name");
 
             b.DECL$setName(n2, t.token.value);
-            b.DECL$setReadOnly(n2, n.type == CONST);
+            b.DECL$setReadOnly(n2, n.type === CONST);
             addDecl.call(b, n, n2, s);
 
             if (t.match(ASSIGN)) {
                 if (t.token.assignOp)
                     throw t.newSyntaxError("Invalid variable initialization");
 
                 // Parse the init as a normal assignment with a fake lhs.
                 var id = new Node(n2.tokenizer, IDENTIFIER);
@@ -1579,17 +1622,17 @@ Narcissus.jsparse = (function() {
         var b = x.builder;
 
         // t.token.type must be LET
         n = b.LET_BLOCK$build(t);
         t.mustMatch(LEFT_PAREN);
         b.LET_BLOCK$setVariables(n, Variables(t, x, n));
         t.mustMatch(RIGHT_PAREN);
 
-        if (isStatement && t.peek() != LEFT_CURLY) {
+        if (isStatement && t.peek() !== LEFT_CURLY) {
             /*
              * If this is really an expression in let statement guise, then we
              * need to wrap the LET_BLOCK node in a SEMICOLON node so that we pop
              * the return value of the expression.
              */
             n2 = b.SEMICOLON$build(t);
             b.SEMICOLON$setExpression(n2, n);
             b.SEMICOLON$finish(n2);
@@ -1605,36 +1648,36 @@ Narcissus.jsparse = (function() {
         }
 
         b.LET_BLOCK$finish(n);
 
         return n;
     }
 
     function checkDestructuring(t, x, n, simpleNamesOnly, data) {
-        if (n.type == ARRAY_COMP)
+        if (n.type === ARRAY_COMP)
             throw t.newSyntaxError("Invalid array comprehension left-hand side");
-        if (n.type != ARRAY_INIT && n.type != OBJECT_INIT)
+        if (n.type !== ARRAY_INIT && n.type !== OBJECT_INIT)
             return;
 
         var b = x.builder;
 
         for (var i = 0, j = n.length; i < j; i++) {
             var nn = n[i], lhs, rhs;
             if (!nn)
                 continue;
-            if (nn.type == PROPERTY_INIT)
+            if (nn.type === PROPERTY_INIT)
                 lhs = nn[0], rhs = nn[1];
             else
                 lhs = null, rhs = null;
-            if (rhs && (rhs.type == ARRAY_INIT || rhs.type == OBJECT_INIT))
+            if (rhs && (rhs.type === ARRAY_INIT || rhs.type === OBJECT_INIT))
                 checkDestructuring(t, x, rhs, simpleNamesOnly, data);
             if (lhs && simpleNamesOnly) {
                 // In declarations, lhs must be simple names
-                if (lhs.type != IDENTIFIER) {
+                if (lhs.type !== IDENTIFIER) {
                     throw t.newSyntaxError("missing name in pattern");
                 } else if (data) {
                     var n2 = b.DECL$build(t);
                     b.DECL$setName(n2, lhs.value);
                     // Don't need to set initializer because it's just for
                     // hoisting anyways.
                     b.DECL$finish(n2);
                     // Each pattern needs to be added to varDecls.
@@ -1668,17 +1711,17 @@ Narcissus.jsparse = (function() {
         body = b.COMP_TAIL$build(t);
 
         do {
             n = b.FOR$build(t);
             // Comprehension tails are always for..in loops.
             b.FOR$rebuildForIn(n);
             if (t.match(IDENTIFIER)) {
                 // But sometimes they're for each..in.
-                if (t.token.value == "each")
+                if (t.token.value === "each")
                     b.FOR$rebuildForEach(n);
                 else
                     t.unget();
             }
             t.mustMatch(LEFT_PAREN);
             switch(t.get()) {
               case LEFT_BRACKET:
               case LEFT_CURLY:
@@ -1729,19 +1772,19 @@ Narcissus.jsparse = (function() {
          */
         var oldLoopInit = x.inForLoopInit;
         x.inForLoopInit = false;
         var n = Expression(t, x);
         x.inForLoopInit = oldLoopInit;
 
         var err = "expression must be parenthesized";
         if (t.match(FOR)) {
-            if (n.type == YIELD && !n.parenthesized)
+            if (n.type === YIELD && !n.parenthesized)
                 throw t.newSyntaxError("Yield " + err);
-            if (n.type == COMMA && !n.parenthesized)
+            if (n.type === COMMA && !n.parenthesized)
                 throw t.newSyntaxError("Generator " + err);
             n = GeneratorExpression(t, x, n);
         }
 
         t.mustMatch(RIGHT_PAREN);
 
         return n;
     }
@@ -1757,17 +1800,17 @@ Narcissus.jsparse = (function() {
 
         n = AssignExpression(t, x);
         if (t.match(COMMA)) {
             n2 = b.COMMA$build(t);
             b.COMMA$addOperand(n2, n);
             n = n2;
             do {
                 n2 = n[n.length-1];
-                if (n2.type == YIELD && !n2.parenthesized)
+                if (n2.type === YIELD && !n2.parenthesized)
                     throw t.newSyntaxError("Yield expression must be parenthesized");
                 b.COMMA$addOperand(n, AssignExpression(t, x));
             } while (t.match(COMMA));
             b.COMMA$finish(n);
         }
 
         return n;
     }
@@ -1940,17 +1983,17 @@ Narcissus.jsparse = (function() {
 
         /*
          * Uses of the in operator in shiftExprs are always unambiguous,
          * so unset the flag that prohibits recognizing it.
          */
         x.inForLoopInit = false;
         n = ShiftExpression(t, x);
         while ((t.match(LT) || t.match(LE) || t.match(GE) || t.match(GT) ||
-               (oldLoopInit == false && t.match(IN)) ||
+               (oldLoopInit === false && t.match(IN)) ||
                t.match(INSTANCEOF))) {
             n2 = b.RELATIONAL$build(t);
             b.RELATIONAL$addOperand(n2, n);
             b.RELATIONAL$addOperand(n2, ShiftExpression(t, x));
             b.RELATIONAL$finish(n2);
             n = n2;
         }
         x.inForLoopInit = oldLoopInit;
@@ -2024,17 +2067,17 @@ Narcissus.jsparse = (function() {
             b.UNARY$addOperand(n, MemberExpression(t, x, true));
             break;
 
           default:
             t.unget();
             n = MemberExpression(t, x, true);
 
             // Don't look across a newline boundary for a postfix {in,de}crement.
-            if (t.tokens[(t.tokenIndex + t.lookahead - 1) & 3].lineno ==
+            if (t.tokens[(t.tokenIndex + t.lookahead - 1) & 3].lineno ===
                 t.lineno) {
                 if (t.match(INCREMENT) || t.match(DECREMENT)) {
                     n2 = b.UNARY$build(t);
                     b.UNARY$setPostfix(n2);
                     b.UNARY$finish(n);
                     b.UNARY$addOperand(n2, n);
                     n = n2;
                 }
@@ -2057,17 +2100,17 @@ Narcissus.jsparse = (function() {
                 b.MEMBER$rebuildNewWithArgs(n);
                 b.MEMBER$addOperand(n, ArgumentList(t, x));
             }
             b.MEMBER$finish(n);
         } else {
             n = PrimaryExpression(t, x);
         }
 
-        while ((tt = t.get()) != END) {
+        while ((tt = t.get()) !== END) {
             switch (tt) {
               case DOT:
                 n2 = b.MEMBER$build(t);
                 b.MEMBER$addOperand(n2, n);
                 t.mustMatch(IDENTIFIER);
                 b.MEMBER$addOperand(n2, b.MEMBER$build(t));
                 break;
 
@@ -2104,21 +2147,21 @@ Narcissus.jsparse = (function() {
         var b = x.builder;
         var err = "expression must be parenthesized";
 
         n = b.LIST$build(t);
         if (t.match(RIGHT_PAREN, true))
             return n;
         do {
             n2 = AssignExpression(t, x);
-            if (n2.type == YIELD && !n2.parenthesized && t.peek() == COMMA)
+            if (n2.type === YIELD && !n2.parenthesized && t.peek() === COMMA)
                 throw t.newSyntaxError("Yield " + err);
             if (t.match(FOR)) {
                 n2 = GeneratorExpression(t, x, n2);
-                if (n.length > 1 || t.peek(true) == COMMA)
+                if (n.length > 1 || t.peek(true) === COMMA)
                     throw t.newSyntaxError("Generator " + err);
             }
             b.LIST$addOperand(n, n2);
         } while (t.match(COMMA));
         t.mustMatch(RIGHT_PAREN);
         b.LIST$finish(n);
 
         return n;
@@ -2130,30 +2173,30 @@ Narcissus.jsparse = (function() {
 
         switch (tt) {
           case FUNCTION:
             n = FunctionDefinition(t, x, false, EXPRESSED_FORM);
             break;
 
           case LEFT_BRACKET:
             n = b.ARRAY_INIT$build(t);
-            while ((tt = t.peek()) != RIGHT_BRACKET) {
-                if (tt == COMMA) {
+            while ((tt = t.peek()) !== RIGHT_BRACKET) {
+                if (tt === COMMA) {
                     t.get();
                     b.ARRAY_INIT$addElement(n, null);
                     continue;
                 }
                 b.ARRAY_INIT$addElement(n, AssignExpression(t, x));
-                if (tt != COMMA && !t.match(COMMA))
+                if (tt !== COMMA && !t.match(COMMA))
                     break;
             }
 
             // If we matched exactly one element and got a FOR, we have an
             // array comprehension.
-            if (n.length == 1 && t.match(FOR)) {
+            if (n.length === 1 && t.match(FOR)) {
                 n2 = b.ARRAY_COMP$build(t);
                 b.ARRAY_COMP$setExpression(n2, n[0]);
                 b.ARRAY_COMP$setTail(n2, comprehensionTail(t, x));
                 n = n2;
             }
             t.mustMatch(RIGHT_BRACKET);
             b.PRIMARY$finish(n);
             break;
@@ -2161,50 +2204,50 @@ Narcissus.jsparse = (function() {
           case LEFT_CURLY:
             var id;
             n = b.OBJECT_INIT$build(t);
 
           object_init:
             if (!t.match(RIGHT_CURLY)) {
                 do {
                     tt = t.get();
-                    if ((t.token.value == "get" || t.token.value == "set") &&
-                        t.peek() == IDENTIFIER) {
+                    if ((t.token.value === "get" || t.token.value === "set") &&
+                        t.peek() === IDENTIFIER) {
                         if (x.ecma3OnlyMode)
                             throw t.newSyntaxError("Illegal property accessor");
                         var fd = FunctionDefinition(t, x, true, EXPRESSED_FORM);
                         b.OBJECT_INIT$addProperty(n, fd);
                     } else {
                         switch (tt) {
                           case IDENTIFIER: case NUMBER: case STRING:
                             id = b.PRIMARY$build(t, IDENTIFIER);
                             b.PRIMARY$finish(id);
                             break;
                           case RIGHT_CURLY:
                             if (x.ecma3OnlyMode)
                                 throw t.newSyntaxError("Illegal trailing ,");
                             break object_init;
                           default:
-                            if (t.token.value in jsdefs.keywords) {
+                            if (t.token.value in definitions.keywords) {
                                 id = b.PRIMARY$build(t, IDENTIFIER);
                                 b.PRIMARY$finish(id);
                                 break;
                             }
                             throw t.newSyntaxError("Invalid property name");
                         }
                         if (t.match(COLON)) {
                             n2 = b.PROPERTY_INIT$build(t);
                             b.PROPERTY_INIT$addOperand(n2, id);
                             b.PROPERTY_INIT$addOperand(n2, AssignExpression(t, x));
                             b.PROPERTY_INIT$finish(n2);
                             b.OBJECT_INIT$addProperty(n, n2);
                         } else {
                             // Support, e.g., |var {x, y} = o| as destructuring shorthand
                             // for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
-                            if (t.peek() != COMMA && t.peek() != RIGHT_CURLY)
+                            if (t.peek() !== COMMA && t.peek() !== RIGHT_CURLY)
                                 throw t.newSyntaxError("missing : after property");
                             b.OBJECT_INIT$addProperty(n, id);
                         }
                     }
                 } while (t.match(COMMA));
                 t.mustMatch(RIGHT_CURLY);
             }
             b.OBJECT_INIT$finish(n);
@@ -2235,28 +2278,28 @@ Narcissus.jsparse = (function() {
 
         return n;
     }
 
     /*
      * parse :: (builder, file ptr, path, line number) -> node
      */
     function parse(b, s, f, l) {
-        var t = new jslex.Tokenizer(s, f, l);
+        var t = new lexer.Tokenizer(s, f, l);
         var x = new StaticContext(false, b);
         var n = Script(t, x);
         if (!t.done)
             throw t.newSyntaxError("Syntax error");
 
         return n;
     }
 
     return {
-        "parse": parse,
-        "VanillaBuilder": VanillaBuilder,
-        "DECLARED_FORM": DECLARED_FORM,
-        "EXPRESSED_FORM": EXPRESSED_FORM,
-        "STATEMENT_FORM": STATEMENT_FORM,
-        "Tokenizer": jslex.Tokenizer,
-        "FunctionDefinition": FunctionDefinition
+        parse: parse,
+        VanillaBuilder: VanillaBuilder,
+        DECLARED_FORM: DECLARED_FORM,
+        EXPRESSED_FORM: EXPRESSED_FORM,
+        STATEMENT_FORM: STATEMENT_FORM,
+        Tokenizer: lexer.Tokenizer,
+        FunctionDefinition: FunctionDefinition
     };
 
 }());
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -288,16 +288,81 @@ ifdef WINCE
 # don't need -c
 AS_DASH_C_FLAG =
 ASFLAGS += -arch 6
 ASFILES += jswince.asm
 endif
 
 endif # ENABLE_TRACEJIT
 
+ifeq ($(TARGET_CPU), powerpc)
+
+VPATH +=	$(srcdir)/assembler \
+		$(srcdir)/assembler/wtf \
+		$(srcdir)/yarr/pcre \
+		$(NULL)
+
+CPPSRCS += 	pcre_compile.cpp \
+                pcre_exec.cpp \
+                pcre_tables.cpp \
+                pcre_xclass.cpp \
+                pcre_ucp_searchfuncs.cpp \
+		$(NULL)
+else
+
+###############################################
+# BEGIN include sources for the Nitro assembler
+#
+VPATH += 	$(srcdir)/assembler \
+		$(srcdir)/assembler/wtf \
+		$(srcdir)/assembler/jit \
+		$(srcdir)/assembler/assembler \
+		$(srcdir)/methodjit \
+		$(srcdir)/yarr \
+		$(srcdir)/yarr/yarr \
+		$(srcdir)/yarr/pcre \
+		$(srcdir)/yarr/wtf \
+		$(NONE)
+
+CPPSRCS += 	Assertions.cpp \
+		ExecutableAllocatorPosix.cpp \
+		ExecutableAllocatorWin.cpp \
+		ExecutableAllocator.cpp \
+		ARMAssembler.cpp \
+                Logging.cpp \
+		MacroAssemblerARM.cpp \
+		MacroAssemblerX86Common.cpp \
+		RegexCompiler.cpp \
+		RegexJIT.cpp \
+		pcre_compile.cpp \
+                pcre_exec.cpp \
+                pcre_tables.cpp \
+                pcre_xclass.cpp \
+                pcre_ucp_searchfuncs.cpp \
+		$(NONE)
+
+ifeq (86, $(findstring 86,$(TARGET_CPU)))
+ifeq (x86_64, $(TARGET_CPU))
+ifeq ($(OS_ARCH),WINNT)
+ASFILES +=	TrampolineMasmX64.asm
+endif
+#CPPSRCS		+= only_on_x86_64.cpp
+else
+#CPPSRCS		+= only_on_x86.cpp
+endif
+endif
+ifeq (arm, $(TARGET_CPU))
+#CPPSRCS		+= only_on_arm.cpp
+endif
+#
+# END enclude sources for the Nitro assembler
+#############################################
+
+endif
+
 ifdef JS_HAS_CTYPES
 VPATH += $(srcdir)/ctypes
 
 CPPSRCS += \
     CTypes.cpp \
     Library.cpp \
     $(NULL)
 
@@ -756,8 +821,23 @@ update-nanojit:
 	           $(CUR_REPO) \
 	           import-revmap
 	(cd $(srcdir) && hg up)
 	(cd $(NANOJIT_CENTRAL_LOCAL) && hg log -r tip --template "{node}\n") >$(srcdir)/nanojit-import-rev
 	(cd $(srcdir) && hg commit --message="Update nanojit-import-rev stamp." nanojit-import-rev)
 
 .PHONY: update-nanojit
 endif
+
+###############################################
+# BEGIN kludges for the Nitro assembler
+#
+
+INCLUDES +=	-I$(srcdir)/assembler -I$(srcdir)/yarr
+#
+# Needed to "configure" it correctly.  Unfortunately these
+# flags wind up being applied to all code in js/src, not just
+# the code in js/src/assembler.
+CXXFLAGS += -DENABLE_ASSEMBLER=1 -DUSE_SYSTEM_MALLOC=1 -DENABLE_JIT=1
+
+#
+# END kludges for the Nitro assembler
+###############################################
new file mode 100644
--- /dev/null
+++ b/js/src/assembler/assembler/ARMAssembler.cpp
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2009 University of Szeged
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "assembler/wtf/Platform.h"
+
+#if ENABLE_ASSEMBLER && WTF_CPU_ARM_TRADITIONAL
+
+#include "ARMAssembler.h"
+
+namespace JSC {
+
+// Patching helpers
+
+void ARMAssembler::patchConstantPoolLoad(void* loadAddr, void* constPoolAddr)
+{
+    ARMWord *ldr = reinterpret_cast<ARMWord*>(loadAddr);
+    ARMWord diff = reinterpret_cast<ARMWord*>(constPoolAddr) - ldr;
+    ARMWord index = (*ldr & 0xfff) >> 1;
+
+    ASSERT(diff >= 1);
+    if (diff >= 2 || index > 0) {
+        diff = (diff + index - 2) * sizeof(ARMWord);
+        ASSERT(diff <= 0xfff);
+        *ldr = (*ldr & ~0xfff) | diff;
+    } else
+        *ldr = (*ldr & ~(0xfff | ARMAssembler::DT_UP)) | sizeof(ARMWord);
+}
+
+// Handle immediates
+
+ARMWord ARMAssembler::getOp2(ARMWord imm)
+{
+    int rol;
+
+    if (imm <= 0xff)
+        return OP2_IMM | imm;
+
+    if ((imm & 0xff000000) == 0) {
+        imm <<= 8;
+        rol = 8;
+    }
+    else {
+        imm = (imm << 24) | (imm >> 8);
+        rol = 0;
+    }
+
+    if ((imm & 0xff000000) == 0) {
+        imm <<= 8;
+        rol += 4;
+    }
+
+    if ((imm & 0xf0000000) == 0) {
+        imm <<= 4;
+        rol += 2;
+    }
+
+    if ((imm & 0xc0000000) == 0) {
+        imm <<= 2;
+        rol += 1;
+    }
+
+    if ((imm & 0x00ffffff) == 0)
+        return OP2_IMM | (imm >> 24) | (rol << 8);
+
+    return INVALID_IMM;
+}
+
+int ARMAssembler::genInt(int reg, ARMWord imm, bool positive)
+{
+    // Step1: Search a non-immediate part
+    ARMWord mask;
+    ARMWord imm1;
+    ARMWord imm2;
+    int rol;
+
+    mask = 0xff000000;
+    rol = 8;
+    while(1) {
+        if ((imm & mask) == 0) {
+            imm = (imm << rol) | (imm >> (32 - rol));
+            rol = 4 + (rol >> 1);
+            break;
+        }
+        rol += 2;
+        mask >>= 2;
+        if (mask & 0x3) {
+            // rol 8
+            imm = (imm << 8) | (imm >> 24);
+            mask = 0xff00;
+            rol = 24;
+            while (1) {
+                if ((imm & mask) == 0) {
+                    imm = (imm << rol) | (imm >> (32 - rol));
+                    rol = (rol >> 1) - 8;
+                    break;
+                }
+                rol += 2;
+                mask >>= 2;
+                if (mask & 0x3)
+                    return 0;
+            }
+            break;
+        }
+    }
+
+    ASSERT((imm & 0xff) == 0);
+
+    if ((imm & 0xff000000) == 0) {
+        imm1 = OP2_IMM | ((imm >> 16) & 0xff) | (((rol + 4) & 0xf) << 8);
+        imm2 = OP2_IMM | ((imm >> 8) & 0xff) | (((rol + 8) & 0xf) << 8);
+    } else if (imm & 0xc0000000) {
+        imm1 = OP2_IMM | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8);
+        imm <<= 8;
+        rol += 4;
+
+        if ((imm & 0xff000000) == 0) {
+            imm <<= 8;
+            rol += 4;
+        }
+
+        if ((imm & 0xf0000000) == 0) {
+            imm <<= 4;
+            rol += 2;
+        }
+
+        if ((imm & 0xc0000000) == 0) {
+            imm <<= 2;
+            rol += 1;
+        }
+
+        if ((imm & 0x00ffffff) == 0)
+            imm2 = OP2_IMM | (imm >> 24) | ((rol & 0xf) << 8);
+        else
+            return 0;
+    } else {
+        if ((imm & 0xf0000000) == 0) {
+            imm <<= 4;
+            rol += 2;
+        }
+
+        if ((imm & 0xc0000000) == 0) {
+            imm <<= 2;
+            rol += 1;
+        }
+
+        imm1 = OP2_IMM | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8);
+        imm <<= 8;
+        rol += 4;
+
+        if ((imm & 0xf0000000) == 0) {
+            imm <<= 4;
+            rol += 2;
+        }
+
+        if ((imm & 0xc0000000) == 0) {
+            imm <<= 2;
+            rol += 1;
+        }
+
+        if ((imm & 0x00ffffff) == 0)
+            imm2 = OP2_IMM | (imm >> 24) | ((rol & 0xf) << 8);
+        else
+            return 0;
+    }
+
+    if (positive) {
+        mov_r(reg, imm1);
+        orr_r(reg, reg, imm2);
+    } else {
+        mvn_r(reg, imm1);
+        bic_r(reg, reg, imm2);
+    }
+
+    return 1;
+}
+
+ARMWord ARMAssembler::getImm(ARMWord imm, int tmpReg, bool invert)
+{
+    ARMWord tmp;
+
+    // Do it by 1 instruction
+    tmp = getOp2(imm);
+    if (tmp != INVALID_IMM)
+        return tmp;
+
+    tmp = getOp2(~imm);
+    if (tmp != INVALID_IMM) {
+        if (invert)
+            return tmp | OP2_INV_IMM;
+        mvn_r(tmpReg, tmp);
+        return tmpReg;
+    }
+
+    return encodeComplexImm(imm, tmpReg);
+}
+
+void ARMAssembler::moveImm(ARMWord imm, int dest)
+{
+    ARMWord tmp;
+
+    // Do it by 1 instruction
+    tmp = getOp2(imm);
+    if (tmp != INVALID_IMM) {
+        mov_r(dest, tmp);
+        return;
+    }
+
+    tmp = getOp2(~imm);
+    if (tmp != INVALID_IMM) {
+        mvn_r(dest, tmp);
+        return;
+    }
+
+    encodeComplexImm(imm, dest);
+}
+
+ARMWord ARMAssembler::encodeComplexImm(ARMWord imm, int dest)
+{
+#if WTF_ARM_ARCH_VERSION >= 7
+    ARMWord tmp = getImm16Op2(imm);
+    if (tmp != INVALID_IMM) {
+        movw_r(dest, tmp);
+        return dest;
+    }
+    movw_r(dest, getImm16Op2(imm & 0xffff));
+    movt_r(dest, getImm16Op2(imm >> 16));
+    return dest;
+#else
+    // Do it by 2 instruction
+    if (genInt(dest, imm, true))
+        return dest;
+    if (genInt(dest, ~imm, false))
+        return dest;
+
+    ldr_imm(dest, imm);
+    return dest;
+#endif
+}
+
+// Memory load/store helpers
+
+void ARMAssembler::dataTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset, bool bytes)
+{
+    ARMWord transferFlag = bytes ? DT_BYTE : 0;
+    if (offset >= 0) {
+        if (offset <= 0xfff)
+            dtr_u(isLoad, srcDst, base, offset | transferFlag);
+        else if (offset <= 0xfffff) {
+            add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8));
+            dtr_u(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff) | transferFlag);
+        } else {
+            ARMWord reg = getImm(offset, ARMRegisters::S0);
+            dtr_ur(isLoad, srcDst, base, reg | transferFlag);
+        }
+    } else {
+        offset = -offset;
+        if (offset <= 0xfff)
+            dtr_d(isLoad, srcDst, base, offset | transferFlag);
+        else if (offset <= 0xfffff) {
+            sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8));
+            dtr_d(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff) | transferFlag);
+        } else {
+            ARMWord reg = getImm(offset, ARMRegisters::S0);
+            dtr_dr(isLoad, srcDst, base, reg | transferFlag);
+        }
+    }
+}
+
+void ARMAssembler::baseIndexTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset)
+{
+    ARMWord op2;
+
+    ASSERT(scale >= 0 && scale <= 3);
+    op2 = lsl(index, scale);
+
+    if (offset >= 0 && offset <= 0xfff) {
+        add_r(ARMRegisters::S0, base, op2);
+        dtr_u(isLoad, srcDst, ARMRegisters::S0, offset);
+        return;
+    }
+    if (offset <= 0 && offset >= -0xfff) {
+        add_r(ARMRegisters::S0, base, op2);
+        dtr_d(isLoad, srcDst, ARMRegisters::S0, -offset);
+        return;
+    }
+
+    ldr_un_imm(ARMRegisters::S0, offset);
+    add_r(ARMRegisters::S0, ARMRegisters::S0, op2);
+    dtr_ur(isLoad, srcDst, base, ARMRegisters::S0);
+}
+
+void ARMAssembler::doubleTransfer(bool isLoad, FPRegisterID srcDst, RegisterID base, int32_t offset)
+{
+    if (offset & 0x3) {
+        if (offset <= 0x3ff && offset >= 0) {
+            fdtr_u(isLoad, srcDst, base, offset >> 2);
+            return;
+        }
+        if (offset <= 0x3ffff && offset >= 0) {
+            add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 10) | (11 << 8));
+            fdtr_u(isLoad, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff);
+            return;
+        }
+        offset = -offset;
+
+        if (offset <= 0x3ff && offset >= 0) {
+            fdtr_d(isLoad, srcDst, base, offset >> 2);
+            return;
+        }
+        if (offset <= 0x3ffff && offset >= 0) {
+            sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 10) | (11 << 8));
+            fdtr_d(isLoad, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff);
+            return;
+        }
+        offset = -offset;
+    }
+
+    // TODO: This is broken in the case that offset is unaligned. VFP can never
+    // perform unaligned accesses, even from an unaligned register base. (NEON
+    // can, but VFP isn't NEON. It is not advisable to interleave a NEON load
+    // with VFP code, so the best solution here is probably to perform an
+    // unaligned integer load, then move the result into VFP using VMOV.)
+    ASSERT((offset & 0x3) == 0);
+
+    ldr_un_imm(ARMRegisters::S0, offset);
+    add_r(ARMRegisters::S0, ARMRegisters::S0, base);
+    fdtr_u(isLoad, srcDst, ARMRegisters::S0, 0);
+}
+
+// Fix up the offsets and literal-pool loads in buffer. The buffer should
+// already contain the code from m_buffer.
+inline void ARMAssembler::fixUpOffsets(void * buffer)
+{
+    char * data = reinterpret_cast<char *>(buffer);
+    for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) {
+        // The last bit is set if the constant must be placed on constant pool.
+        int pos = (*iter) & (~0x1);
+        ARMWord* ldrAddr = reinterpret_cast<ARMWord*>(data + pos);
+        ARMWord* addr = getLdrImmAddress(ldrAddr);
+        if (*addr != InvalidBranchTarget) {
+            if (!(*iter & 1)) {
+                int diff = reinterpret_cast<ARMWord*>(data + *addr) - (ldrAddr + DefaultPrefetching);
+
+                if ((diff <= BOFFSET_MAX && diff >= BOFFSET_MIN)) {
+                    *ldrAddr = B | getConditionalField(*ldrAddr) | (diff & BRANCH_MASK);
+                    continue;
+                }
+            }
+            *addr = reinterpret_cast<ARMWord>(data + *addr);
+        }
+    }
+}
+
+void* ARMAssembler::executableCopy(ExecutablePool* allocator)
+{
+    // 64-bit alignment is required for next constant pool and JIT code as well
+    m_buffer.flushWithoutBarrier(true);
+    if (m_buffer.uncheckedSize() & 0x7)
+        bkpt(0);
+
+    void * data = m_buffer.executableCopy(allocator);
+    fixUpOffsets(data);
+    return data;
+}
+
+// This just dumps the code into the specified buffer, fixing up absolute
+// offsets and literal pool loads as it goes. The buffer is assumed to be large
+// enough to hold the code, and any pre-existing literal pool is assumed to
+// have been flushed.
+void* ARMAssembler::executableCopy(void * buffer)
+{
+    ASSERT(m_buffer.sizeOfConstantPool() == 0);
+
+    memcpy(buffer, m_buffer.data(), m_buffer.size());
+    fixUpOffsets(buffer);
+    return buffer;
+}
+
+} // namespace JSC
+
+#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)
new file mode 100644
--- /dev/null
+++ b/js/src/assembler/assembler/ARMAssembler.h
@@ -0,0 +1,1321 @@
+/*
+ * Copyright (C) 2009, 2010 University of Szeged
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ARMAssembler_h
+#define ARMAssembler_h
+
+#include "assembler/wtf/Platform.h"
+
+// Some debug code uses s(n)printf for instruction logging.
+#include <stdio.h>
+
+#if ENABLE_ASSEMBLER && WTF_CPU_ARM_TRADITIONAL
+
+#include "AssemblerBufferWithConstantPool.h"
+#include "assembler/wtf/Assertions.h"
+
+#include "methodjit/Logging.h"
+#define IPFX    "        %s"
+#define ISPFX   "        "
+#ifdef JS_METHODJIT_SPEW
+# define MAYBE_PAD (isOOLPath ? ">  " : "")
+# define FIXME_INSN_PRINTING                                \
+    do {                                                   \
+        js::JaegerSpew(js::JSpew_Insns,                    \
+                       IPFX "FIXME insn printing %s:%d\n", \
+                       MAYBE_PAD,                          \
+                       __FILE__, __LINE__);                \
+    } while (0)
+#else
+# define MAYBE_PAD ""
+# define FIXME_INSN_PRINTING ((void) 0)
+#endif
+
+// TODO: We don't print the condition code in our JaegerSpew lines. Doing this
+// is awkward whilst maintaining a consistent field width.
+
+namespace JSC {
+
+    typedef uint32_t ARMWord;
+
+    namespace ARMRegisters {
+        typedef enum {
+            r0 = 0,
+            r1,
+            r2,
+            r3,
+            S0 = r3,
+            r4,
+            r5,
+            r6,
+            r7,
+            r8,
+            S1 = r8,
+            r9,
+            r10,
+            r11,
+            r12,
+            ip = r12,
+            r13,
+            sp = r13,
+            r14,
+            lr = r14,
+            r15,
+            pc = r15
+        } RegisterID;
+
+        typedef enum {
+            d0,
+            d1,
+            d2,
+            d3,
+            SD0 = d3,
+            d4,
+            d5,
+            d6,
+            d7,
+            d8,
+            d9,
+            d10,
+            d11,
+            d12,
+            d13,
+            d14,
+            d15,
+            d16,
+            d17,
+            d18,
+            d19,
+            d20,
+            d21,
+            d22,
+            d23,
+            d24,
+            d25,
+            d26,
+            d27,
+            d28,
+            d29,
+            d30,
+            d31
+        } FPRegisterID;
+
+    } // namespace ARMRegisters
+
+    class ARMAssembler {
+    public:
+        
+#ifdef DEBUG
+        bool isOOLPath;
+        // Assign a default value to keep Valgrind quiet.
+        ARMAssembler() : isOOLPath(false) { }
+#else
+        ARMAssembler() { }
+#endif
+
+        typedef ARMRegisters::RegisterID RegisterID;
+        typedef ARMRegisters::FPRegisterID FPRegisterID;
+        typedef AssemblerBufferWithConstantPool<2048, 4, 4, ARMAssembler> ARMBuffer;
+        typedef SegmentedVector<int, 64> Jumps;
+
+        unsigned char *buffer() const { return m_buffer.buffer(); }
+
+        // ARM conditional constants
+        typedef enum {
+            EQ = 0x00000000, // Zero
+            NE = 0x10000000, // Non-zero
+            CS = 0x20000000,
+            CC = 0x30000000,
+            MI = 0x40000000,
+            PL = 0x50000000,
+            VS = 0x60000000,
+            VC = 0x70000000,
+            HI = 0x80000000,
+            LS = 0x90000000,
+            GE = 0xa0000000,
+            LT = 0xb0000000,
+            GT = 0xc0000000,
+            LE = 0xd0000000,
+            AL = 0xe0000000
+        } Condition;
+
+        // ARM instruction constants
+        enum {
+            AND = (0x0 << 21),
+            EOR = (0x1 << 21),
+            SUB = (0x2 << 21),
+            RSB = (0x3 << 21),
+            ADD = (0x4 << 21),
+            ADC = (0x5 << 21),
+            SBC = (0x6 << 21),
+            RSC = (0x7 << 21),
+            TST = (0x8 << 21),
+            TEQ = (0x9 << 21),
+            CMP = (0xa << 21),
+            CMN = (0xb << 21),
+            ORR = (0xc << 21),
+            MOV = (0xd << 21),
+            BIC = (0xe << 21),
+            MVN = (0xf << 21),
+            MUL = 0x00000090,
+            MULL = 0x00c00090,
+            FCPYD = 0x0eb00b40,
+            FADDD = 0x0e300b00,
+            FNEGD = 0x0eb10b40,
+            FDIVD = 0x0e800b00,
+            FSUBD = 0x0e300b40,
+            FMULD = 0x0e200b00,
+            FCMPD = 0x0eb40b40,
+            FSQRTD = 0x0eb10bc0,
+            DTR = 0x05000000,
+            LDRH = 0x00100090,
+            STRH = 0x00000090,
+            STMDB = 0x09200000,
+            LDMIA = 0x08b00000,
+            FDTR = 0x0d000b00,
+            B = 0x0a000000,
+            BL = 0x0b000000
+#if WTF_ARM_ARCH_VERSION >= 5 || defined(__ARM_ARCH_4T__)
+           ,BX = 0x012fff10
+#endif
+           ,FMSR = 0x0e000a10,
+            FMRS = 0x0e100a10,
+            FSITOD = 0x0eb80bc0,
+            FTOSID = 0x0ebd0b40,
+            FMSTAT = 0x0ef1fa10
+#if WTF_ARM_ARCH_VERSION >= 5
+           ,CLZ = 0x016f0f10,
+            BKPT = 0xe120070,
+            BLX = 0x012fff30
+#endif
+#if WTF_ARM_ARCH_VERSION >= 7
+           ,MOVW = 0x03000000,
+            MOVT = 0x03400000
+#endif
+        };
+
+        enum {
+            OP2_IMM = (1 << 25),
+            OP2_IMMh = (1 << 22),
+            OP2_INV_IMM = (1 << 26),
+            SET_CC = (1 << 20),
+            OP2_OFSREG = (1 << 25),
+            DT_UP = (1 << 23),
+            DT_BYTE = (1 << 22),
+            DT_WB = (1 << 21),
+            // This flag is inlcuded in LDR and STR
+            DT_PRE = (1 << 24),
+            HDT_UH = (1 << 5),
+            DT_LOAD = (1 << 20)
+        };
+
+        // Masks of ARM instructions
+        enum {
+            BRANCH_MASK = 0x00ffffff,
+            NONARM = 0xf0000000,
+            SDT_MASK = 0x0c000000,
+            SDT_OFFSET_MASK = 0xfff
+        };
+
+        enum {
+            BOFFSET_MIN = -0x00800000,
+            BOFFSET_MAX = 0x007fffff,
+            SDT = 0x04000000
+        };
+
+        enum {
+            padForAlign8  = 0x00,
+            padForAlign16 = 0x0000,
+            padForAlign32 = 0xee120070
+        };
+
+        typedef enum {
+            LSL = 0,
+            LSR = 1,
+            ASR = 2,
+            ROR = 3
+        } Shift;
+
+        static const ARMWord INVALID_IMM = 0xf0000000;
+        static const ARMWord InvalidBranchTarget = 0xffffffff;
+        static const int DefaultPrefetching = 2;
+
+        class JmpSrc {
+            friend class ARMAssembler;
+        public:
+            JmpSrc()
+                : m_offset(-1)
+            {
+            }
+
+        private:
+            JmpSrc(int offset)
+                : m_offset(offset)
+            {
+            }
+
+            int m_offset;
+        };
+
+        class JmpDst {
+            friend class ARMAssembler;
+        public:
+            JmpDst()
+                : m_offset(-1)
+                , m_used(false)
+            {
+            }
+
+            bool isUsed() const { return m_used; }
+            void used() { m_used = true; }
+            bool isValid() const { return m_offset != -1; }
+        private:
+            JmpDst(int offset)
+                : m_offset(offset)
+                , m_used(false)
+            {
+                ASSERT(m_offset == offset);
+            }
+
+            int m_offset : 31;
+            int m_used : 1;
+        };
+
+        // Instruction formating
+
+        void emitInst(ARMWord op, int rd, int rn, ARMWord op2)
+        {
+            ASSERT ( ((op2 & ~OP2_IMM) <= 0xfff) || (((op2 & ~OP2_IMMh) <= 0xfff)) );
+            m_buffer.putInt(op | RN(rn) | RD(rd) | op2);
+        }
+
+        void and_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("and", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | AND, rd, rn, op2);
+        }
+
+        void ands_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("ands", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | AND | SET_CC, rd, rn, op2);
+        }
+
+        void eor_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("eor", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | EOR, rd, rn, op2);
+        }
+
+        void eors_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("eors", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | EOR | SET_CC, rd, rn, op2);
+        }
+
+        void sub_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("sub", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | SUB, rd, rn, op2);
+        }
+
+        void subs_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("subs", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | SUB | SET_CC, rd, rn, op2);
+        }
+
+        void rsb_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("rsb", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | RSB, rd, rn, op2);
+        }
+
+        void rsbs_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("rsbs", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | RSB | SET_CC, rd, rn, op2);
+        }
+
+        void add_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("add", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | ADD, rd, rn, op2);
+        }
+
+        void adds_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("adds", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | ADD | SET_CC, rd, rn, op2);
+        }
+
+        void adc_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("adc", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | ADC, rd, rn, op2);
+        }
+
+        void adcs_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("adcs", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | ADC | SET_CC, rd, rn, op2);
+        }
+
+        void sbc_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("sbc", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | SBC, rd, rn, op2);
+        }
+
+        void sbcs_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("sbcs", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | SBC | SET_CC, rd, rn, op2);
+        }
+
+        void rsc_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("rsc", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | RSC, rd, rn, op2);
+        }
+
+        void rscs_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("rscs", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | RSC | SET_CC, rd, rn, op2);
+        }
+
+        void tst_r(int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("tst", cc, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | TST | SET_CC, 0, rn, op2);
+        }
+
+        void teq_r(int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("teq", cc, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | TEQ | SET_CC, 0, rn, op2);
+        }
+
+        void cmp_r(int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("cmp", cc, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | CMP | SET_CC, 0, rn, op2);
+        }
+
+        void orr_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("orr", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | ORR, rd, rn, op2);
+        }
+
+        void orrs_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("orrs", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | ORR | SET_CC, rd, rn, op2);
+        }
+
+        void mov_r(int rd, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("mov", cc, rd, op2);
+            emitInst(static_cast<ARMWord>(cc) | MOV, rd, ARMRegisters::r0, op2);
+        }
+
+#if WTF_ARM_ARCH_VERSION >= 7
+        void movw_r(int rd, ARMWord op2, Condition cc = AL)
+        {
+            ASSERT((op2 | 0xf0fff) == 0xf0fff);
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX    "%-15s %s, 0x%04x\n", MAYBE_PAD, "movw", nameGpReg(rd), (op2 & 0xfff) | ((op2 >> 4) & 0xf000));
+            m_buffer.putInt(static_cast<ARMWord>(cc) | MOVW | RD(rd) | op2);
+        }
+
+        void movt_r(int rd, ARMWord op2, Condition cc = AL)
+        {
+            ASSERT((op2 | 0xf0fff) == 0xf0fff);
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX    "%-15s %s, 0x%04x\n", MAYBE_PAD, "movt", nameGpReg(rd), (op2 & 0xfff) | ((op2 >> 4) & 0xf000));
+            m_buffer.putInt(static_cast<ARMWord>(cc) | MOVT | RD(rd) | op2);
+        }
+#endif
+
+        void movs_r(int rd, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("movs", cc, rd, op2);
+            emitInst(static_cast<ARMWord>(cc) | MOV | SET_CC, rd, ARMRegisters::r0, op2);
+        }
+
+        void bic_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("bic", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | BIC, rd, rn, op2);
+        }
+
+        void bics_r(int rd, int rn, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("bics", cc, rd, rn, op2);
+            emitInst(static_cast<ARMWord>(cc) | BIC | SET_CC, rd, rn, op2);
+        }
+
+        void mvn_r(int rd, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("mvn", cc, rd, op2);
+            emitInst(static_cast<ARMWord>(cc) | MVN, rd, ARMRegisters::r0, op2);
+        }
+
+        void mvns_r(int rd, ARMWord op2, Condition cc = AL)
+        {
+            spewInsWithOp2("mvns", cc, rd, op2);
+            emitInst(static_cast<ARMWord>(cc) | MVN | SET_CC, rd, ARMRegisters::r0, op2);
+        }
+
+        void mul_r(int rd, int rn, int rm, Condition cc = AL)
+        {
+            spewInsWithOp2("mul", cc, rd, rn, static_cast<ARMWord>(rm));
+            m_buffer.putInt(static_cast<ARMWord>(cc) | MUL | RN(rd) | RS(rn) | RM(rm));
+        }
+
+        void muls_r(int rd, int rn, int rm, Condition cc = AL)
+        {
+            spewInsWithOp2("muls", cc, rd, rn, static_cast<ARMWord>(rm));
+            m_buffer.putInt(static_cast<ARMWord>(cc) | MUL | SET_CC | RN(rd) | RS(rn) | RM(rm));
+        }
+
+        void mull_r(int rdhi, int rdlo, int rn, int rm, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, %s, %s, %s\n", MAYBE_PAD, "mull", nameGpReg(rdlo), nameGpReg(rdhi), nameGpReg(rn), nameGpReg(rm));
+            m_buffer.putInt(static_cast<ARMWord>(cc) | MULL | RN(rdhi) | RD(rdlo) | RS(rn) | RM(rm));
+        }
+
+        void fcpyd_r(int dd, int dm, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, "vmov.f64", nameFpRegD(dd), nameFpRegD(dm));
+            // TODO: emitInst doesn't work for VFP instructions, though it
+            // seems to work for current usage.
+            emitInst(static_cast<ARMWord>(cc) | FCPYD, dd, dd, dm);
+        }
+
+        void faddd_r(int dd, int dn, int dm, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, "vadd.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
+            // TODO: emitInst doesn't work for VFP instructions, though it
+            // seems to work for current usage.
+            emitInst(static_cast<ARMWord>(cc) | FADDD, dd, dn, dm);
+        }
+
+        void fnegd_r(int dd, int dm, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, %s, %s, %s\n", MAYBE_PAD, "fnegd", nameFpRegD(dd), nameFpRegD(dm));
+            m_buffer.putInt(static_cast<ARMWord>(cc) | FNEGD | DD(dd) | DM(dm));
+        }
+
+        void fdivd_r(int dd, int dn, int dm, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, "vdiv.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
+            // TODO: emitInst doesn't work for VFP instructions, though it
+            // seems to work for current usage.
+            emitInst(static_cast<ARMWord>(cc) | FDIVD, dd, dn, dm);
+        }
+
+        void fsubd_r(int dd, int dn, int dm, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, "vsub.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
+            // TODO: emitInst doesn't work for VFP instructions, though it
+            // seems to work for current usage.
+            emitInst(static_cast<ARMWord>(cc) | FSUBD, dd, dn, dm);
+        }
+
+        void fmuld_r(int dd, int dn, int dm, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, "vmul.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
+            // TODO: emitInst doesn't work for VFP instructions, though it
+            // seems to work for current usage.
+            emitInst(static_cast<ARMWord>(cc) | FMULD, dd, dn, dm);
+        }
+
+        void fcmpd_r(int dd, int dm, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, %s\n", MAYBE_PAD, "vcmp.f64", nameFpRegD(dd), nameFpRegD(dm));
+            // TODO: emitInst doesn't work for VFP instructions, though it
+            // seems to work for current usage.
+            emitInst(static_cast<ARMWord>(cc) | FCMPD, dd, 0, dm);
+        }
+
+        void fsqrtd_r(int dd, int dm, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, %s\n", MAYBE_PAD, "vsqrt.f64", nameFpRegD(dd), nameFpRegD(dm));
+            // TODO: emitInst doesn't work for VFP instructions, though it
+            // seems to work for current usage.
+            emitInst(static_cast<ARMWord>(cc) | FSQRTD, dd, 0, dm);
+        }
+
+        void ldr_imm(int rd, ARMWord imm, Condition cc = AL)
+        {
+            char mnemonic[16];
+            snprintf(mnemonic, 16, "ldr%s", nameCC(cc));
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX    "%-15s %s, =0x%x @ (%d) (reusable pool entry)\n", MAYBE_PAD, mnemonic, nameGpReg(rd), imm, static_cast<int32_t>(imm));
+            m_buffer.putIntWithConstantInt(static_cast<ARMWord>(cc) | DTR | DT_LOAD | DT_UP | RN(ARMRegisters::pc) | RD(rd), imm, true);
+        }
+
+        void ldr_un_imm(int rd, ARMWord imm, Condition cc = AL)
+        {
+            char mnemonic[16];
+            snprintf(mnemonic, 16, "ldr%s", nameCC(cc));
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX    "%-15s %s, =0x%x @ (%d)\n", MAYBE_PAD, mnemonic, nameGpReg(rd), imm, static_cast<int32_t>(imm));
+            m_buffer.putIntWithConstantInt(static_cast<ARMWord>(cc) | DTR | DT_LOAD | DT_UP | RN(ARMRegisters::pc) | RD(rd), imm);
+        }
+
+        // Data transfers like this:
+        //  LDR rd, [rb, +offset]
+        //  STR rd, [rb, +offset]
+        void dtr_u(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL)
+        {
+            char const * mnemonic = (isLoad) ? ("ldr") : ("str");
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
+            emitInst(static_cast<ARMWord>(cc) | DTR | (isLoad ? DT_LOAD : 0) | DT_UP, rd, rb, offset);
+        }
+
+        // Data transfers like this:
+        //  LDR rd, [rb, +rm]
+        //  STR rd, [rb, +rm]
+        void dtr_ur(bool isLoad, int rd, int rb, int rm, Condition cc = AL)
+        {
+            char const * mnemonic = (isLoad) ? ("ldr") : ("str");
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, [%s, +%s]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
+            emitInst(static_cast<ARMWord>(cc) | DTR | (isLoad ? DT_LOAD : 0) | DT_UP | OP2_OFSREG, rd, rb, rm);
+        }
+
+        // Data transfers like this:
+        //  LDR rd, [rb, -offset]
+        //  STR rd, [rb, -offset]
+        void dtr_d(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL)
+        {
+            char const * mnemonic = (isLoad) ? ("ldr") : ("str");
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
+            emitInst(static_cast<ARMWord>(cc) | DTR | (isLoad ? DT_LOAD : 0), rd, rb, offset);
+        }
+
+        // Data transfers like this:
+        //  LDR rd, [rb, -rm]
+        //  STR rd, [rb, -rm]
+        void dtr_dr(bool isLoad, int rd, int rb, int rm, Condition cc = AL)
+        {
+            char const * mnemonic = (isLoad) ? ("ldr") : ("str");
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, [%s, -%s]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
+            emitInst(static_cast<ARMWord>(cc) | DTR | (isLoad ? DT_LOAD : 0) | OP2_OFSREG, rd, rb, rm);
+        }
+
+        void ldrh_r(int rd, int rb, int rm, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, [%s, +%s]\n", MAYBE_PAD, "ldrh", nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
+            emitInst(static_cast<ARMWord>(cc) | LDRH | HDT_UH | DT_UP | DT_PRE, rd, rb, rm);
+        }
+
+        void ldrh_d(int rd, int rb, ARMWord offset, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, "ldrh", nameGpReg(rd), nameGpReg(rb), offset);
+            emitInst(static_cast<ARMWord>(cc) | LDRH | HDT_UH | DT_PRE, rd, rb, offset);
+        }
+
+        void ldrh_u(int rd, int rb, ARMWord offset, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, "ldrh", nameGpReg(rd), nameGpReg(rb), offset);
+            emitInst(static_cast<ARMWord>(cc) | LDRH | HDT_UH | DT_UP | DT_PRE, rd, rb, offset);
+        }
+
+        void strh_r(int rb, int rm, int rd, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, [%s, +%s]\n", MAYBE_PAD, "strh", nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
+            emitInst(static_cast<ARMWord>(cc) | STRH | HDT_UH | DT_UP | DT_PRE, rd, rb, rm);
+        }
+
+        void fdtr_u(bool isLoad, int dd, int rn, ARMWord offset, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, "vldr.f64", nameFpRegD(dd), nameGpReg(rn), offset);
+            ASSERT(offset <= 0xff);
+            emitInst(static_cast<ARMWord>(cc) | FDTR | DT_UP | (isLoad ? DT_LOAD : 0), dd, rn, offset);
+        }
+
+        void fdtr_d(bool isLoad, int dd, int rn, ARMWord offset, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, "vldr.f64", nameFpRegD(dd), nameGpReg(rn), offset);
+            ASSERT(offset <= 0xff);
+            emitInst(static_cast<ARMWord>(cc) | FDTR | (isLoad ? DT_LOAD : 0), dd, rn, offset);
+        }
+
+        void push_r(int reg, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s {%s}\n", MAYBE_PAD, "push", nameGpReg(reg));
+            ASSERT(ARMWord(reg) <= 0xf);
+            m_buffer.putInt(cc | DTR | DT_WB | RN(ARMRegisters::sp) | RD(reg) | 0x4);
+        }
+
+        void pop_r(int reg, Condition cc = AL)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s {%s}\n", MAYBE_PAD, "pop", nameGpReg(reg));
+            ASSERT(ARMWord(reg) <= 0xf);
+            m_buffer.putInt(cc | (DTR ^ DT_PRE) | DT_LOAD | DT_UP | RN(ARMRegisters::sp) | RD(reg) | 0x4);
+        }
+
+        inline void poke_r(int reg, Condition cc = AL)
+        {
+            dtr_d(false, ARMRegisters::sp, 0, reg, cc);
+        }
+
+        inline void peek_r(int reg, Condition cc = AL)
+        {
+            dtr_u(true, reg, ARMRegisters::sp, 0, cc);
+        }
+
+        void fmsr_r(int dd, int rn, Condition cc = AL)
+        {
+            // TODO: emitInst doesn't work for VFP instructions, though it
+            // seems to work for current usage.
+            emitInst(static_cast<ARMWord>(cc) | FMSR, rn, dd, 0);
+        }
+
+        void fmrs_r(int rd, int dn, Condition cc = AL)
+        {
+            // TODO: emitInst doesn't work for VFP instructions, though it
+            // seems to work for current usage.
+            emitInst(static_cast<ARMWord>(cc) | FMRS, rd, dn, 0);
+        }
+
+        void fsitod_r(int dd, int dm, Condition cc = AL)
+        {
+            // TODO: emitInst doesn't work for VFP instructions, though it
+            // seems to work for current usage.
+            emitInst(static_cast<ARMWord>(cc) | FSITOD, dd, 0, dm);
+        }
+
+        void ftosid_r(int fd, int dm, Condition cc = AL)
+        {
+            // TODO: emitInst doesn't work for VFP instructions, though it
+            // seems to work for current usage.
+            emitInst(static_cast<ARMWord>(cc) | FTOSID, fd, 0, dm);
+        }
+
+        void fmstat(Condition cc = AL)
+        {
+            // TODO: emitInst doesn't work for VFP instructions, though it
+            // seems to work for current usage.
+            m_buffer.putInt(static_cast<ARMWord>(cc) | FMSTAT);
+        }
+
+#if WTF_ARM_ARCH_VERSION >= 5
+        void clz_r(int rd, int rm, Condition cc = AL)
+        {
+            spewInsWithOp2("clz", cc, rd, static_cast<ARMWord>(rm));
+            m_buffer.putInt(static_cast<ARMWord>(cc) | CLZ | RD(rd) | RM(rm));
+        }
+#endif
+
+        void bkpt(ARMWord value)
+        {
+#if WTF_ARM_ARCH_VERSION >= 5
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s #0x%04x\n", MAYBE_PAD, "bkpt", value);
+            m_buffer.putInt(BKPT | ((value & 0xfff0) << 4) | (value & 0xf));
+#else
+            // Cannot access to Zero memory address
+            dtr_dr(true, ARMRegisters::S0, ARMRegisters::S0, ARMRegisters::S0);
+#endif
+        }
+
+        void bx(int rm, Condition cc = AL)
+        {
+#if WTF_ARM_ARCH_VERSION >= 5 || defined(__ARM_ARCH_4T__)
+            js::JaegerSpew(
+                    js::JSpew_Insns,
+                    IPFX    "bx%-13s %s\n", MAYBE_PAD, nameCC(cc), nameGpReg(rm));
+            emitInst(static_cast<ARMWord>(cc) | BX, 0, 0, RM(rm));
+#else
+            mov_r(ARMRegisters::pc, RM(rm), cc);
+#endif
+        }
+
+        JmpSrc blx(int rm, Condition cc = AL)
+        {
+#if WTF_ARM_ARCH_AT_LEAST(5)
+            int s = m_buffer.uncheckedSize();
+            js::JaegerSpew(
+                    js::JSpew_Insns,
+                    IPFX    "blx%-12s %s\n", MAYBE_PAD, nameCC(cc), nameGpReg(rm));
+            emitInst(static_cast<ARMWord>(cc) | BLX, 0, 0, RM(rm));
+#else
+            ASSERT(rm != 14);
+            ensureSpace(2 * sizeof(ARMWord), 0);
+            mov_r(ARMRegisters::lr, ARMRegisters::pc, cc);
+            int s = m_buffer.uncheckedSize();
+            bx(rm, cc);
+#endif
+            return JmpSrc(s);
+        }
+
+        static ARMWord lsl(int reg, ARMWord value)
+        {
+            ASSERT(reg <= ARMRegisters::pc);
+            ASSERT(value <= 0x1f);
+            return reg | (value << 7) | (LSL << 5);
+        }
+
+        static ARMWord lsr(int reg, ARMWord value)
+        {
+            ASSERT(reg <= ARMRegisters::pc);
+            ASSERT(value <= 0x1f);
+            return reg | (value << 7) | (LSR << 5);
+        }
+
+        static ARMWord asr(int reg, ARMWord value)
+        {
+            ASSERT(reg <= ARMRegisters::pc);
+            ASSERT(value <= 0x1f);
+            return reg | (value << 7) | (ASR << 5);
+        }
+
+        static ARMWord lsl_r(int reg, int shiftReg)
+        {
+            ASSERT(reg <= ARMRegisters::pc);
+            ASSERT(shiftReg <= ARMRegisters::pc);
+            return reg | (shiftReg << 8) | (LSL << 5) | 0x10;
+        }
+
+        static ARMWord lsr_r(int reg, int shiftReg)
+        {
+            ASSERT(reg <= ARMRegisters::pc);
+            ASSERT(shiftReg <= ARMRegisters::pc);
+            return reg | (shiftReg << 8) | (LSR << 5) | 0x10;
+        }
+
+        static ARMWord asr_r(int reg, int shiftReg)
+        {
+            ASSERT(reg <= ARMRegisters::pc);
+            ASSERT(shiftReg <= ARMRegisters::pc);
+            return reg | (shiftReg << 8) | (ASR << 5) | 0x10;
+        }
+
+        // General helpers
+
+        void forceFlushConstantPool()
+        {
+            m_buffer.flushWithoutBarrier(true);
+        }
+
+        int size()
+        {
+            return m_buffer.size();
+        }
+
+        void ensureSpace(int insnSpace, int constSpace)
+        {
+            m_buffer.ensureSpace(insnSpace, constSpace);
+        }
+
+        int sizeOfConstantPool()
+        {
+            return m_buffer.sizeOfConstantPool();
+        }
+
+        JmpDst label()
+        {
+            return JmpDst(m_buffer.size());
+        }
+
+        JmpDst align(int alignment)
+        {
+            while (!m_buffer.isAligned(alignment))
+                mov_r(ARMRegisters::r0, ARMRegisters::r0);
+
+            return label();
+        }
+
+        JmpSrc loadBranchTarget(int rd, Condition cc = AL, int useConstantPool = 0)
+        {
+            ensureSpace(sizeof(ARMWord), sizeof(ARMWord));
+            int s = m_buffer.uncheckedSize();
+            ldr_un_imm(rd, InvalidBranchTarget, cc);
+            m_jumps.append(s | (useConstantPool & 0x1));
+            return JmpSrc(s);
+        }
+
+        JmpSrc jmp(Condition cc = AL, int useConstantPool = 0)
+        {
+            return loadBranchTarget(ARMRegisters::pc, cc, useConstantPool);
+        }
+
+        void* executableCopy(ExecutablePool* allocator);
+        void* executableCopy(void* buffer);
+        void fixUpOffsets(void* buffer);
+
+        // Patching helpers
+
+        static ARMWord* getLdrImmAddress(ARMWord* insn)
+        {
+#if WTF_ARM_ARCH_AT_LEAST(5)
+            // Check for call
+            if ((*insn & 0x0f7f0000) != 0x051f0000) {
+                // Must be BLX
+                ASSERT((*insn & 0x012fff30) == 0x012fff30);
+                insn--;
+            }
+#endif
+            // Must be an ldr ..., [pc +/- imm]
+            ASSERT((*insn & 0x0f7f0000) == 0x051f0000);
+
+            ARMWord addr = reinterpret_cast<ARMWord>(insn) + DefaultPrefetching * sizeof(ARMWord);
+            if (*insn & DT_UP)
+                return reinterpret_cast<ARMWord*>(addr + (*insn & SDT_OFFSET_MASK));
+            return reinterpret_cast<ARMWord*>(addr - (*insn & SDT_OFFSET_MASK));
+        }
+
+        static ARMWord* getLdrImmAddressOnPool(ARMWord* insn, uint32_t* constPool)
+        {
+            // Must be an ldr ..., [pc +/- imm]
+            ASSERT((*insn & 0x0f7f0000) == 0x051f0000);
+
+            if (*insn & 0x1)
+                return reinterpret_cast<ARMWord*>(constPool + ((*insn & SDT_OFFSET_MASK) >> 1));
+            return getLdrImmAddress(insn);
+        }
+
+        static void patchPointerInternal(intptr_t from, void* to)
+        {
+            ARMWord* insn = reinterpret_cast<ARMWord*>(from);
+            ARMWord* addr = getLdrImmAddress(insn);
+            *addr = reinterpret_cast<ARMWord>(to);
+        }
+
+        static ARMWord patchConstantPoolLoad(ARMWord load, ARMWord value)
+        {
+            value = (value << 1) + 1;
+            ASSERT(!(value & ~0xfff));
+            return (load & ~0xfff) | value;
+        }
+
+        static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr);
+
+        // Patch pointers
+
+        static void linkPointer(void* code, JmpDst from, void* to)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                           ISPFX "##linkPointer     ((%p + %#x)) points to ((%p))\n",
+                           code, from.m_offset, to);
+
+            patchPointerInternal(reinterpret_cast<intptr_t>(code) + from.m_offset, to);
+        }
+
+        static void repatchInt32(void* from, int32_t to)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                           ISPFX "##repatchInt32    ((%p)) holds ((%p))\n",
+                           from, to);
+
+            patchPointerInternal(reinterpret_cast<intptr_t>(from), reinterpret_cast<void*>(to));
+        }
+
+        static void repatchPointer(void* from, void* to)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                           ISPFX "##repatchPointer  ((%p)) points to ((%p))\n",
+                           from, to);
+
+            patchPointerInternal(reinterpret_cast<intptr_t>(from), to);
+        }
+
+        static void repatchLoadPtrToLEA(void* from)
+        {
+            // On arm, this is a patch from LDR to ADD. It is restricted conversion,
+            // from special case to special case, altough enough for its purpose
+            ARMWord* insn = reinterpret_cast<ARMWord*>(from);
+            ASSERT((*insn & 0x0ff00f00) == 0x05900000);
+
+            *insn = (*insn & 0xf00ff0ff) | 0x02800000;
+            ExecutableAllocator::cacheFlush(insn, sizeof(ARMWord));
+        }
+
+        static void repatchLEAToLoadPtr(void* from)
+        {
+	    // Like repatchLoadPtrToLEA, this is specialized for our purpose.
+            ARMWord* insn = reinterpret_cast<ARMWord*>(from);
+	    if ((*insn & 0x0ff00f00) == 0x05900000)
+		return;
+            ASSERT((*insn & 0xf00ff0ff) == 0x02800000);
+
+            *insn = (*insn &  0x0ff00f00) | 0x05900000;
+            ExecutableAllocator::cacheFlush(insn, sizeof(ARMWord));
+        }
+
+        // Linkers
+
+        void linkJump(JmpSrc from, JmpDst to)
+        {
+            ARMWord  code = reinterpret_cast<ARMWord>(m_buffer.data());
+            ARMWord* insn = reinterpret_cast<ARMWord*>(code + from.m_offset);
+            ARMWord* addr = getLdrImmAddressOnPool(insn, m_buffer.poolAddress());
+
+            js::JaegerSpew(js::JSpew_Insns,
+                           IPFX "##linkJump         ((%#x)) jumps to ((%#x))\n", MAYBE_PAD,
+                           from.m_offset, to.m_offset);
+
+            *addr = to.m_offset;
+        }
+
+        static void linkJump(void* code, JmpSrc from, void* to)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                           ISPFX "##linkJump        ((%p + %#x)) jumps to ((%p))\n",
+                           code, from.m_offset, to);
+
+            patchPointerInternal(reinterpret_cast<intptr_t>(code) + from.m_offset, to);
+        }
+
+        static void relinkJump(void* from, void* to)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                           ISPFX "##relinkJump      ((%p)) jumps to ((%p))\n",
+                           from, to);
+
+            patchPointerInternal(reinterpret_cast<intptr_t>(from) - sizeof(ARMWord), to);
+        }
+
+        static void linkCall(void* code, JmpSrc from, void* to)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                           ISPFX "##linkCall        ((%p + %#x)) jumps to ((%p))\n",
+                           code, from.m_offset, to);
+
+            patchPointerInternal(reinterpret_cast<intptr_t>(code) + from.m_offset, to);
+        }
+
+        static void relinkCall(void* from, void* to)
+        {
+            js::JaegerSpew(js::JSpew_Insns,
+                           ISPFX "##relinkCall      ((%p)) jumps to ((%p))\n",
+                           from, to);
+
+            patchPointerInternal(reinterpret_cast<intptr_t>(from) - sizeof(ARMWord), to);
+        }
+
+        // Address operations
+
+        static void* getRelocatedAddress(void* code, JmpSrc jump)
+        {
+            return reinterpret_cast<void*>(reinterpret_cast<ARMWord*>(code) + jump.m_offset / sizeof(ARMWord) + 1);
+        }
+
+        static void* getRelocatedAddress(void* code, JmpDst label)
+        {
+            return reinterpret_cast<void*>(reinterpret_cast<ARMWord*>(code) + label.m_offset / sizeof(ARMWord));
+        }
+
+        // Address differences
+
+        static int getDifferenceBetweenLabels(JmpDst from, JmpSrc to)
+        {
+            return (to.m_offset + sizeof(ARMWord)) - from.m_offset;
+        }
+
+        static int getDifferenceBetweenLabels(JmpDst from, JmpDst to)
+        {
+            return to.m_offset - from.m_offset;
+        }
+
+        static unsigned getCallReturnOffset(JmpSrc call)
+        {
+            return call.m_offset + sizeof(ARMWord);
+        }
+
+        // Handle immediates
+
+        static ARMWord getOp2Byte(ARMWord imm)
+        {
+            ASSERT(imm <= 0xff);
+            return OP2_IMMh | (imm & 0x0f) | ((imm & 0xf0) << 4) ;
+        }
+
+        static ARMWord getOp2(ARMWord imm);
+
+#if WTF_ARM_ARCH_VERSION >= 7
+        static ARMWord getImm16Op2(ARMWord imm)
+        {
+            if (imm <= 0xffff)
+                return (imm & 0xf000) << 4 | (imm & 0xfff);
+            return INVALID_IMM;
+        }
+#endif
+        ARMWord getImm(ARMWord imm, int tmpReg, bool invert = false);
+        void moveImm(ARMWord imm, int dest);
+        ARMWord encodeComplexImm(ARMWord imm, int dest);
+
+        ARMWord getOffsetForHalfwordDataTransfer(ARMWord imm, int tmpReg)
+        {
+            // Encode immediate data in the instruction if it is possible
+            if (imm <= 0xff)
+                return getOp2Byte(imm);
+            // Otherwise, store the data in a temporary register
+            return encodeComplexImm(imm, tmpReg);
+        }
+
+        // Memory load/store helpers
+
+        void dataTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset, bool bytes = false);
+        void baseIndexTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset);
+        void doubleTransfer(bool isLoad, FPRegisterID srcDst, RegisterID base, int32_t offset);
+
+        // Constant pool hnadlers
+
+        static ARMWord placeConstantPoolBarrier(int offset)
+        {
+            offset = (offset - sizeof(ARMWord)) >> 2;
+            ASSERT((offset <= BOFFSET_MAX && offset >= BOFFSET_MIN));
+            return AL | B | (offset & BRANCH_MASK);
+        }
+
+    private:
+        static char const * nameGpReg(int reg)
+        {
+            ASSERT(reg <= 16);
+            ASSERT(reg >= 0);
+            static char const * names[] = {
+                "r0", "r1", "r2", "r3",
+                "r4", "r5", "r6", "r7",
+                "r8", "r9", "r10", "r11",
+                "ip", "sp", "lr", "pc"
+            };
+            return names[reg];
+        }
+
+        static char const * nameFpRegD(int reg)
+        {
+            ASSERT(reg <= 31);
+            ASSERT(reg >= 0);
+            static char const * names[] = {
+                 "d0",   "d1",   "d2",   "d3",
+                 "d4",   "d5",   "d6",   "d7",
+                 "d8",   "d9",  "d10",  "d11",
+                "d12",  "d13",  "d14",  "d15",
+                "d16",  "d17",  "d18",  "d19",
+                "d20",  "d21",  "d22",  "d23",
+                "d24",  "d25",  "d26",  "d27",
+                "d28",  "d29",  "d30",  "d31"
+            };
+            return names[reg];
+        }
+
+        static char const * nameCC(Condition cc)
+        {
+            ASSERT(cc <= AL);
+            ASSERT(cc >= 0);
+            ASSERT((cc & 0x0fffffff) == 0);
+
+            uint32_t    ccIndex = cc >> 28;
+            static char const * names[] = {
+                "eq", "ne",
+                "cs", "cc",
+                "mi", "pl",
+                "vs", "vc",
+                "hi", "ls",
+                "ge", "lt",
+                "gt", "le",
+                "  "        // AL is the default, so don't show it.
+            };
+            return names[ccIndex];
+        }
+
+        // Decodes operand 2 immediate values (for debug output and assertions).
+        inline uint32_t decOp2Imm(uint32_t op2)
+        {
+            ASSERT((op2 & ~0xfff) == 0);
+
+            uint32_t    imm8 = op2 & 0xff;
+            uint32_t    rot = 32 - ((op2 >> 7) & 0x1e);
+
+            return imm8 << (rot & 0x1f);
+        }
+
+        // Format the operand 2 argument for debug spew. The operand can be
+        // either an immediate or a register specifier.
+        void fmtOp2(char * out, ARMWord op2)
+        {
+            static char const * const shifts[4] = {"LSL", "LSR", "ASR", "ROR"};
+
+            if ((op2 & OP2_IMM) || (op2 & OP2_IMMh)) {
+                // Immediate values.
+                
+                uint32_t    imm = decOp2Imm(op2 & ~(OP2_IMM | OP2_IMMh));
+                sprintf(out, "#0x%x @ (%d)", imm, static_cast<int32_t>(imm));
+            } else {
+                // Register values.
+
+                char const *    rm = nameGpReg(op2 & 0xf);
+                Shift           type = static_cast<Shift>((op2 >> 5) & 0x3);
+
+                // Bit 4 specifies barrel-shifter parameters in operand 2.
+                if (op2 & (1<<4)) {
+                    // Register-shifted register.
+                    // Example: "r0, LSL r6"
+                    char const *    rs = nameGpReg((op2 >> 8) & 0xf);
+                    sprintf(out, "%s, %s %s", rm, shifts[type], rs);
+                } else {
+                    // Immediate-shifted register.
+                    // Example: "r0, ASR #31"
+                    uint32_t        imm = (op2 >> 7) & 0x1f;
+                    
+                    // Deal with special encodings.
+                    if ((type == LSL) && (imm == 0)) {
+                        // "LSL #0" doesn't shift at all (and is the default).
+                        sprintf(out, rm);
+                        return;
+                    }
+
+                    if ((type == ROR) && (imm == 0)) {
+                        // "ROR #0" is a special case ("RRX").
+                        sprintf(out, "%s, RRX", rm);
+                        return;
+                    }
+
+                    if (((type == LSR) || (type == ASR)) && (imm == 0)) {
+                        // Both LSR and ASR have a range of 1-32, with 32
+                        // encoded as 0.                  
+                        imm = 32;
+                    }
+
+                    // Print the result.
+
+                    sprintf(out, "%s, %s #%u", rm, shifts[type], imm);
+                }
+            }
+        }
+
+        void spewInsWithOp2(char const * ins, Condition cc, int rd, int rn, ARMWord op2)
+        {
+            char    mnemonic[16];
+            snprintf(mnemonic, 16, "%s%s", ins, nameCC(cc));
+
+            char    op2_fmt[48];
+            fmtOp2(op2_fmt, op2);
+
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rn), op2_fmt);
+        }
+
+        void spewInsWithOp2(char const * ins, Condition cc, int r, ARMWord op2)
+        {
+            char    mnemonic[16];
+            snprintf(mnemonic, 16, "%s%s", ins, nameCC(cc));
+
+            char    op2_fmt[48];
+            fmtOp2(op2_fmt, op2);
+
+            js::JaegerSpew(js::JSpew_Insns,
+                    IPFX   "%-15s %s, %s\n", MAYBE_PAD, mnemonic, nameGpReg(r), op2_fmt);
+        }
+
+        ARMWord RM(int reg)
+        {
+            ASSERT(reg <= ARMRegisters::pc);
+            return reg;
+        }
+
+        ARMWord RS(int reg)
+        {
+            ASSERT(reg <= ARMRegisters::pc);
+            return reg << 8;
+        }
+
+        ARMWord RD(int reg)
+        {
+            ASSERT(reg <= ARMRegisters::pc);
+            return reg << 12;
+        }
+
+        ARMWord RN(int reg)
+        {
+            ASSERT(reg <= ARMRegisters::pc);
+            return reg << 16;
+        }
+
+        ARMWord DD(int reg)
+        {
+            ASSERT(reg <= ARMRegisters::d31);
+            // Endoded as bits [22,15:12].
+            return ((reg << 12) | (reg << 18)) & 0x0040f000;
+        }
+
+        ARMWord DN(int reg)
+        {
+            ASSERT(reg <= ARMRegisters::d31);
+            // Endoded as bits [7,19:16].
+            return ((reg << 16) | (reg << 3)) & 0x000f0080;
+        }
+
+        ARMWord DM(int reg)
+        {
+            ASSERT(reg <= ARMRegisters::d31);
+            // Encoded as bits [5,3:0].
+            return ((reg << 1) & 0x20) | (reg & 0xf);
+        }
+
+        static ARMWord getConditionalField(ARMWord i)
+        {
+            return i & 0xf0000000;
+        }
+
+        int genInt(int reg, ARMWord imm, bool positive);
+
+        ARMBuffer m_buffer;
+        Jumps m_jumps;
+    };
+
+} // namespace JSC
+
+#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)
+
+#endif // ARMAssembler_h
new file mode 100644
--- /dev/null
+++ b/js/src/assembler/assembler/ARMv7Assembler.h
@@ -0,0 +1,1916 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 University of Szeged
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef ARMAssembler_h
+#define ARMAssembler_h
+
+#include "assembler/wtf/Platform.h"
+
+#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2)
+
+#include "AssemblerBuffer.h"
+#include "assembler/wtf/Assertions.h"
+#include "assembler/wtf/Vector.h"
+#include <stdint.h>
+
+namespace JSC {
+
+namespace ARMRegisters {
+    typedef enum {
+        r0,
+        r1,
+        r2,
+        r3,
+        r4,
+        r5,
+        r6,
+        r7, wr = r7,   // thumb work register
+        r8,
+        r9, sb = r9,   // static base
+        r10, sl = r10, // stack limit
+        r11, fp = r11, // frame pointer
+        r12, ip = r12,
+        r13, sp = r13,
+        r14, lr = r14,
+        r15, pc = r15,
+    } RegisterID;
+
+    // s0 == d0 == q0
+    // s4 == d2 == q1
+    // etc
+    typedef enum {
+        s0 = 0,
+        s1 = 1,
+        s2 = 2,
+        s3 = 3,
+        s4 = 4,
+        s5 = 5,
+        s6 = 6,
+        s7 = 7,
+        s8 = 8,
+        s9 = 9,
+        s10 = 10,
+        s11 = 11,
+        s12 = 12,
+        s13 = 13,
+        s14 = 14,
+        s15 = 15,
+        s16 = 16,
+        s17 = 17,
+        s18 = 18,
+        s19 = 19,
+        s20 = 20,
+        s21 = 21,
+        s22 = 22,
+        s23 = 23,
+        s24 = 24,
+        s25 = 25,
+        s26 = 26,
+        s27 = 27,
+        s28 = 28,
+        s29 = 29,
+        s30 = 30,
+        s31 = 31,
+        d0 = 0 << 1,
+        d1 = 1 << 1,
+        d2 = 2 << 1,
+        d3 = 3 << 1,
+        d4 = 4 << 1,
+        d5 = 5 << 1,
+        d6 = 6 << 1,
+        d7 = 7 << 1,
+        d8 = 8 << 1,
+        d9 = 9 << 1,
+        d10 = 10 << 1,
+        d11 = 11 << 1,
+        d12 = 12 << 1,
+        d13 = 13 << 1,
+        d14 = 14 << 1,
+        d15 = 15 << 1,
+        d16 = 16 << 1,
+        d17 = 17 << 1,
+        d18 = 18 << 1,
+        d19 = 19 << 1,
+        d20 = 20 << 1,
+        d21 = 21 << 1,
+        d22 = 22 << 1,
+        d23 = 23 << 1,
+        d24 = 24 << 1,
+        d25 = 25 << 1,
+        d26 = 26 << 1,
+        d27 = 27 << 1,
+        d28 = 28 << 1,
+        d29 = 29 << 1,
+        d30 = 30 << 1,
+        d31 = 31 << 1,
+        q0 = 0 << 2,
+        q1 = 1 << 2,
+        q2 = 2 << 2,
+        q3 = 3 << 2,
+        q4 = 4 << 2,
+        q5 = 5 << 2,
+        q6 = 6 << 2,
+        q7 = 7 << 2,
+        q8 = 8 << 2,
+        q9 = 9 << 2,
+        q10 = 10 << 2,
+        q11 = 11 << 2,
+        q12 = 12 << 2,
+        q13 = 13 << 2,
+        q14 = 14 << 2,
+        q15 = 15 << 2,
+        q16 = 16 << 2,
+        q17 = 17 << 2,
+        q18 = 18 << 2,
+        q19 = 19 << 2,
+        q20 = 20 << 2,
+        q21 = 21 << 2,
+        q22 = 22 << 2,
+        q23 = 23 << 2,
+        q24 = 24 << 2,
+        q25 = 25 << 2,
+        q26 = 26 << 2,
+        q27 = 27 << 2,
+        q28 = 28 << 2,
+        q29 = 29 << 2,
+        q30 = 30 << 2,
+        q31 = 31 << 2,
+    } FPRegisterID;
+}
+
+class ARMv7Assembler;
+class ARMThumbImmediate {
+    friend class ARMv7Assembler;
+
+    typedef uint8_t ThumbImmediateType;
+    static const ThumbImmediateType TypeInvalid = 0;
+    static const ThumbImmediateType TypeEncoded = 1;
+    static const ThumbImmediateType TypeUInt16 = 2;
+
+    typedef union {
+        int16_t asInt;
+        struct {
+            unsigned imm8 : 8;
+            unsigned imm3 : 3;
+            unsigned i    : 1;
+            unsigned imm4 : 4;
+        };
+        // If this is an encoded immediate, then it may describe a shift, or a pattern.
+        struct {
+            unsigned shiftValue7 : 7;
+            unsigned shiftAmount : 5;
+        };
+        struct {
+            unsigned immediate   : 8;
+            unsigned pattern     : 4;
+        };
+    } ThumbImmediateValue;
+
+    // byte0 contains least significant bit; not using an array to make client code endian agnostic.
+    typedef union {
+        int32_t asInt;
+        struct {
+            uint8_t byte0;
+            uint8_t byte1;
+            uint8_t byte2;
+            uint8_t byte3;
+        };
+    } PatternBytes;
+
+    ALWAYS_INLINE static void countLeadingZerosPartial(uint32_t& value, int32_t& zeros, const int N)
+    {
+        if (value & ~((1 << N) - 1)) /* check for any of the top N bits (of 2N bits) are set */
+            value >>= N;             /* if any were set, lose the bottom N */
+        else                         /* if none of the top N bits are set, */
+            zeros += N;              /* then we have identified N leading zeros */
+    }
+
+    static int32_t countLeadingZeros(uint32_t value)
+    {
+        if (!value)
+            return 32;
+
+        int32_t zeros = 0;
+        countLeadingZerosPartial(value, zeros, 16);
+        countLeadingZerosPartial(value, zeros, 8);
+        countLeadingZerosPartial(value, zeros, 4);
+        countLeadingZerosPartial(value, zeros, 2);
+        countLeadingZerosPartial(value, zeros, 1);
+        return zeros;
+    }
+
+    ARMThumbImmediate()
+        : m_type(TypeInvalid)
+    {
+        m_value.asInt = 0;
+    }
+        
+    ARMThumbImmediate(ThumbImmediateType type, ThumbImmediateValue value)
+        : m_type(type)
+        , m_value(value)
+    {
+    }
+
+    ARMThumbImmediate(ThumbImmediateType type, uint16_t value)
+        : m_type(TypeUInt16)
+    {
+        // Make sure this constructor is only reached with type TypeUInt16;
+        // this extra parameter makes the code a little clearer by making it
+        // explicit at call sites which type is being constructed
+        ASSERT_UNUSED(type, type == TypeUInt16);
+
+        m_value.asInt = value;
+    }
+
+public:
+    static ARMThumbImmediate makeEncodedImm(uint32_t value)
+    {
+        ThumbImmediateValue encoding;
+        encoding.asInt = 0;
+
+        // okay, these are easy.
+        if (value < 256) {
+            encoding.immediate = value;
+            encoding.pattern = 0;
+            return ARMThumbImmediate(TypeEncoded, encoding);
+        }
+
+        int32_t leadingZeros = countLeadingZeros(value);
+        // if there were 24 or more leading zeros, then we'd have hit the (value < 256) case.
+        ASSERT(leadingZeros < 24);
+
+        // Given a number with bit fields Z:B:C, where count(Z)+count(B)+count(C) == 32,
+        // Z are the bits known zero, B is the 8-bit immediate, C are the bits to check for
+        // zero.  count(B) == 8, so the count of bits to be checked is 24 - count(Z).
+        int32_t rightShiftAmount = 24 - leadingZeros;
+        if (value == ((value >> rightShiftAmount) << rightShiftAmount)) {
+            // Shift the value down to the low byte position.  The assign to 
+            // shiftValue7 drops the implicit top bit.
+            encoding.shiftValue7 = value >> rightShiftAmount;
+            // The endoded shift amount is the magnitude of a right rotate.
+            encoding.shiftAmount = 8 + leadingZeros;
+            return ARMThumbImmediate(TypeEncoded, encoding);
+        }
+        
+        PatternBytes bytes;
+        bytes.asInt = value;
+
+        if ((bytes.byte0 == bytes.byte1) && (bytes.byte0 == bytes.byte2) && (bytes.byte0 == bytes.byte3)) {
+            encoding.immediate = bytes.byte0;
+            encoding.pattern = 3;
+            return ARMThumbImmediate(TypeEncoded, encoding);
+        }
+
+        if ((bytes.byte0 == bytes.byte2) && !(bytes.byte1 | bytes.byte3)) {
+            encoding.immediate = bytes.byte0;
+            encoding.pattern = 1;
+            return ARMThumbImmediate(TypeEncoded, encoding);
+        }