Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Mon, 13 Feb 2012 12:32:58 -0800
changeset 105837 3f4000f220f6a69a303f8f8f092dc65bf582b9a2
parent 105836 73866db4e189cb5ca4c232262be99214e087e448 (current diff)
parent 86713 817614044d46191a7914a70a01edce82fb37ba6e (diff)
child 105838 ea5d5e46a2e58fc4fe913f90c03eb929b1f40b80
push id1075
push uservporof@mozilla.com
push dateThu, 13 Sep 2012 10:46:49 +0000
treeherderfx-team@f39786e8364d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone13.0a1
Merge from mozilla-central.
accessible/public/msaa/nsIAccessibleWin32Object.idl
accessible/src/base/NotificationController.cpp
accessible/src/base/nsAccessNode.cpp
accessible/src/base/nsAccessNode.h
accessible/src/base/nsAccessibilityService.cpp
accessible/src/base/nsAccessibilityService.h
accessible/src/base/nsAccessible.cpp
accessible/src/base/nsDocAccessible.cpp
accessible/src/base/nsRootAccessible.cpp
accessible/src/html/nsHyperTextAccessible.cpp
accessible/src/msaa/nsAccessibleWrap.cpp
accessible/tests/mochitest/common.js
accessible/tests/mochitest/events/test_caretmove.html
browser/devtools/styleinspector/CssHtmlTree.jsm
browser/devtools/styleinspector/CssLogic.jsm
config/autoconf.mk.in
configure.in
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsGenericElement.cpp
content/base/test/Makefile.in
content/events/src/nsDOMEventTargetHelper.cpp
content/html/content/src/nsHTMLInputElement.cpp
content/xbl/src/nsXBLDocumentInfo.cpp
content/xbl/src/nsXBLDocumentInfo.h
gfx/layers/basic/BasicLayers.cpp
image/encoders/bmp/nsBMPEncoder.cpp
image/encoders/jpeg/nsJPEGEncoder.cpp
image/encoders/jpeg/nsJPEGEncoder.h
js/src/Makefile.in
js/src/ion/CodeGenerator.cpp
js/src/jsanalyze.cpp
js/src/jsapi.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsprvtd.h
js/src/jsreflect.cpp
js/src/jsstr.cpp
js/src/jsutil.h
js/src/jsxdrapi.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/StubCalls.cpp
js/src/shell/js.cpp
js/src/vm/RegExpObject-inl.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
js/xpconnect/src/XPCInlines.h
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/XPCWrappedNativeScope.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/wrappers/WrapperFactory.cpp
layout/base/crashtests/crashtests.list
layout/base/nsCSSFrameConstructor.cpp
layout/generic/nsColumnSetFrame.cpp
layout/generic/nsHTMLReflowState.h
layout/style/nsCSSValue.cpp
netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
toolkit/components/telemetry/tests/unit/test_TelemetryPing.js
widget/xpwidgets/PuppetWidget.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsStackWalk.cpp
--- a/accessible/public/msaa/Makefile.in
+++ b/accessible/public/msaa/Makefile.in
@@ -44,17 +44,16 @@ LIBRARY_NAME	= AccessibleMarshal
 MODULE    = accessibility
 XPIDL_MODULE = accessibility-msaa
 GRE_MODULE   = 1
 DEFFILE = $(win_srcdir)/AccessibleMarshal.def
 
 include $(DEPTH)/config/autoconf.mk
 
 XPIDLSRCS = \
-      nsIAccessibleWin32Object.idl \
       nsIWinAccessNode.idl \
       $(NULL)
 
 DEFINES += -DREGISTER_PROXY_DLL
 
 GARBAGE += $(MIDL_GENERATED_FILES) done_gen dlldata.c
 
 FORCE_SHARED_LIB = 1
deleted file mode 100644
--- a/accessible/public/msaa/nsIAccessibleWin32Object.idl
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * ***** 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 the Mozilla browser.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1999
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 "nsISupports.idl"
-
-[scriptable, uuid(ca7a3a93-822f-4cdf-8cb4-c52d16b9afc7)]
-interface nsIAccessibleWin32Object : nsISupports
-{
-  /** handle to the external window implementing IAccessible */
-  [noscript] readonly attribute voidPtr hwnd;
-};
--- a/accessible/src/base/FocusManager.cpp
+++ b/accessible/src/base/FocusManager.cpp
@@ -282,21 +282,22 @@ FocusManager::ProcessFocusEvent(AccEvent
 
   EIsFromUserInput fromUserInputFlag = aEvent->IsFromUserInput() ?
     eFromUserInput : eNoUserInput;
 
   // Emit focus event if event target is the active item. Otherwise then check
   // if it's still focused and then update active item and emit focus event.
   nsAccessible* target = aEvent->GetAccessible();
   if (target != mActiveItem) {
+
     // Check if still focused. Otherwise we can end up with storing the active
     // item for control that isn't focused anymore.
-    nsAccessible* DOMFocus =
-      GetAccService()->GetAccessibleOrContainer(FocusedDOMNode(),
-	                                        aEvent->GetDocAccessible());
+    nsDocAccessible* document = aEvent->GetDocAccessible();
+    nsAccessible* DOMFocus = document->GetAccessibleOrContainer(FocusedDOMNode());
+
     if (target != DOMFocus)
       return;
 
     nsAccessible* activeItem = target->CurrentItem();
     if (activeItem) {
       mActiveItem = activeItem;
       target = activeItem;
     }
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -664,19 +664,18 @@ NotificationController::CoalesceTextChan
   }
 
   aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
 }
 
 void
 NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent)
 {
-  nsAccessible* container =
-    GetAccService()->GetContainerAccessible(aEvent->mNode,
-                                            aEvent->GetDocAccessible());
+  nsDocAccessible* document = aEvent->GetDocAccessible();
+  nsAccessible* container = document->GetContainerAccessible(aEvent->mNode);
   if (!container)
     return;
 
   nsHyperTextAccessible* textAccessible = container->AsHyperText();
   if (!textAccessible)
     return;
 
   // Don't fire event for the first html:br in an editor.
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -278,48 +278,16 @@ nsAccessNode::ScrollTo(PRUint32 aScrollT
     return;
 
   PRInt16 vPercent, hPercent;
   nsCoreUtils::ConvertScrollTypeToPercents(aScrollType, &vPercent, &hPercent);
   shell->ScrollContentIntoView(content, vPercent, hPercent,
                                nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
 }
 
-// nsAccessNode public
-already_AddRefed<nsINode>
-nsAccessNode::GetCurrentFocus()
-{
-  // XXX: consider to use nsFocusManager directly, it allows us to avoid
-  // unnecessary query interface calls.
-  nsIDocument* doc = GetDocumentNode();
-  NS_ENSURE_TRUE(doc, nsnull);
-
-  nsIDOMWindow* win = doc->GetWindow();
-
-  nsCOMPtr<nsIDOMWindow> focusedWindow;
-  nsCOMPtr<nsIDOMElement> focusedElement;
-  nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
-  if (fm)
-    fm->GetFocusedElementForWindow(win, true, getter_AddRefs(focusedWindow),
-                                   getter_AddRefs(focusedElement));
-
-  nsINode *focusedNode = nsnull;
-  if (focusedElement) {
-    CallQueryInterface(focusedElement, &focusedNode);
-  }
-  else if (focusedWindow) {
-    nsCOMPtr<nsIDOMDocument> doc;
-    focusedWindow->GetDocument(getter_AddRefs(doc));
-    if (doc)
-      CallQueryInterface(doc, &focusedNode);
-  }
-
-  return focusedNode;
-}
-
 void
 nsAccessNode::Language(nsAString& aLanguage)
 {
   aLanguage.Truncate();
 
   if (IsDefunct())
     return;
 
--- a/accessible/src/base/nsAccessNode.h
+++ b/accessible/src/base/nsAccessNode.h
@@ -91,24 +91,16 @@ public:
   nsDocAccessible* Document() const { return mDoc; }
 
   /**
    * Return the root document accessible for this accessnode.
    */
   nsRootAccessible* RootAccessible() const;
 
   /**
-   * Return focused node within accessible window.
-   *
-   * XXX: it shouldn't break us if we return focused node not depending on
-   * window so that we can turn this method into util method.
-   */
-  already_AddRefed<nsINode> GetCurrentFocus();
-
-  /**
    * Initialize the access node object, add it to the cache.
    */
   virtual bool Init();
 
   /**
    * Shutdown the access node object.
    */
   virtual void Shutdown();
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -914,27 +914,16 @@ nsAccessibilityService::GetAccessible(ns
 {
   NS_PRECONDITION(aNode, "Getting an accessible for null node! Crash.");
 
   // XXX handle the presshell
   nsDocAccessible* document = GetDocAccessible(aNode->OwnerDoc());
   return document ? document->GetAccessible(aNode) : nsnull;
 }
 
-nsAccessible*
-nsAccessibilityService::GetAccessibleOrContainer(nsINode* aNode, nsDocAccessible* aDoc)
-{
-  if (!aNode)
-    return nsnull;
-
-  NS_PRECONDITION(aDoc, "Must pass a document accessible.");
-
-  return aDoc ? aDoc->GetAccessibleOrContainer(aNode) : nsnull;
-}
-
 static bool HasRelatedContent(nsIContent *aContent)
 {
   nsAutoString id;
   if (!aContent || !nsCoreUtils::GetID(aContent, id) || id.IsEmpty()) {
     return false;
   }
 
   // If the given ID is referred by relation attribute then create an accessible
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -192,34 +192,16 @@ public:
                                       bool* aIsSubtreeHidden = nsnull);
 
   /**
    * Return an accessible for the given DOM node and eventually a presentation
    * shell.
    */
   nsAccessible* GetAccessible(nsINode* aNode, nsIPresShell* aPresShell);
 
-  /**
-   * Return an accessible for the given DOM node or container accessible if
-   * the node is not accessible.
-   *
-   * @param aNode [in] the given node
-   * @param aDoc  [in] the document accessible. Can't be null.
-   */
-  nsAccessible* GetAccessibleOrContainer(nsINode* aNode, nsDocAccessible* aDoc);
-
-  /**
-   * Return a container accessible for the given DOM node.
-   */
-  nsAccessible* GetContainerAccessible(nsINode* aNode, nsDocAccessible* aDoc)
-  {
-    return aNode ?
-      GetAccessibleOrContainer(aNode->GetNodeParent(), aDoc) : nsnull;
-  }
-
 private:
   // nsAccessibilityService creation is controlled by friend
   // NS_GetAccessibilityService, keep constructors private.
   nsAccessibilityService();
   nsAccessibilityService(const nsAccessibilityService&);
   nsAccessibilityService& operator =(const nsAccessibilityService&);
 
 private:
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -847,18 +847,17 @@ nsAccessible::ChildAtPoint(PRInt32 aX, P
   nsIFrame *foundFrame = presShell->GetFrameForPoint(frame, offset);
 
   nsIContent* content = nsnull;
   if (!foundFrame || !(content = foundFrame->GetContent()))
     return fallbackAnswer;
 
   // Get accessible for the node with the point or the first accessible in
   // the DOM parent chain.
-  nsAccessible* accessible =
-    GetAccService()->GetAccessibleOrContainer(content, accDocument);
+  nsAccessible* accessible = accDocument->GetAccessibleOrContainer(content);
   if (!accessible)
     return fallbackAnswer;
 
   if (accessible == this) {
     // Manually walk through accessible children and see if the are within this
     // point. Skip offscreen or invisible accessibles. This takes care of cases
     // where layout won't walk into things for us, such as image map areas and
     // sub documents (XXX: subdocuments should be handled by methods of
@@ -1142,17 +1141,17 @@ nsAccessible::TakeFocus()
       if (widgetFrame && widgetFrame->IsFocusable()) {
         focusContent = widgetElm;
         widget->SetCurrentItem(this);
       }
     }
   }
 
   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(focusContent));
-  nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
+  nsFocusManager* fm = nsFocusManager::GetFocusManager();
   if (fm)
     fm->SetFocus(element, 0);
 
   return NS_OK;
 }
 
 nsresult
 nsAccessible::GetHTMLName(nsAString& aLabel)
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -373,17 +373,17 @@ nsDocAccessible::FocusedChild()
 }
 
 NS_IMETHODIMP nsDocAccessible::TakeFocus()
 {
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   // Focus the document.
-  nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
+  nsFocusManager* fm = nsFocusManager::GetFocusManager();
   NS_ENSURE_STATE(fm);
 
   nsCOMPtr<nsIDOMElement> newFocus;
   return fm->MoveFocus(mDocument->GetWindow(), nsnull,
                        nsIFocusManager::MOVEFOCUS_ROOT, 0,
                        getter_AddRefs(newFocus));
 }
 
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -198,17 +198,17 @@ nsRootAccessible::NativeState()
     // XXX unless it's minimized or maximized, but not sure
     //     how to detect that
   if (chromeFlags & nsIWebBrowserChrome::CHROME_TITLEBAR)
     states |= states::MOVEABLE;
   if (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)
     states |= states::MODAL;
 #endif
 
-  nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
+  nsFocusManager* fm = nsFocusManager::GetFocusManager();
   if (fm) {
     nsCOMPtr<nsIDOMWindow> rootWindow;
     GetWindow(getter_AddRefs(rootWindow));
 
     nsCOMPtr<nsIDOMWindow> activeWindow;
     fm->GetActiveWindow(getter_AddRefs(activeWindow));
     if (activeWindow == rootWindow)
       states |= states::ACTIVE;
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -1623,18 +1623,26 @@ nsHyperTextAccessible::SetCaretOffset(PR
 }
 
 /*
  * Gets the offset position of the caret (cursor).
  */
 NS_IMETHODIMP
 nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset)
 {
+  NS_ENSURE_ARG_POINTER(aCaretOffset);
   *aCaretOffset = -1;
 
+  // Not focused focusable accessible except document accessible doesn't have
+  // a caret.
+  if (!IsDoc() && !FocusMgr()->IsFocused(this) &&
+      (State() & states::FOCUSABLE)) {
+    return NS_OK;
+  }
+
   // No caret if the focused node is not inside this DOM node and this DOM node
   // is not inside of focused node.
   FocusManager::FocusDisposition focusDisp =
     FocusMgr()->IsInOrContainsFocus(this);
   if (focusDisp == FocusManager::eNone)
     return NS_OK;
 
   // Turn the focus node and offset of the selection into caret hypretext
--- a/accessible/src/msaa/nsAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsAccessibleWrap.cpp
@@ -45,17 +45,16 @@
 #include "Relation.h"
 #include "Role.h"
 #include "States.h"
 
 #include "ia2AccessibleRelation.h"
 
 #include "nsIAccessibleEvent.h"
 #include "nsIAccessibleRelation.h"
-#include "nsIAccessibleWin32Object.h"
 
 #include "Accessible2_i.c"
 #include "AccessibleStates.h"
 
 #include "nsIMutableArray.h"
 #include "nsIDOMDocument.h"
 #include "nsIFrame.h"
 #include "nsIScrollableFrame.h"
@@ -1679,39 +1678,26 @@ nsAccessibleWrap::ConvertToIA2Attributes
 
   if (strAttrs.IsEmpty())
     return S_FALSE;
 
   *aIA2Attributes = ::SysAllocStringLen(strAttrs.get(), strAttrs.Length());
   return *aIA2Attributes ? S_OK : E_OUTOFMEMORY;
 }
 
-IDispatch *nsAccessibleWrap::NativeAccessible(nsIAccessible *aXPAccessible)
+IDispatch*
+nsAccessibleWrap::NativeAccessible(nsIAccessible* aAccessible)
 {
-  if (!aXPAccessible) {
-   NS_WARNING("Not passing in an aXPAccessible");
+  if (!aAccessible) {
+   NS_WARNING("Not passing in an aAccessible");
    return NULL;
   }
 
-  nsCOMPtr<nsIAccessibleWin32Object> accObject(do_QueryInterface(aXPAccessible));
-  if (accObject) {
-    void* hwnd = nsnull;
-    accObject->GetHwnd(&hwnd);
-    if (hwnd) {
-      IDispatch *retval = nsnull;
-      ::AccessibleObjectFromWindow(reinterpret_cast<HWND>(hwnd),
-                                   OBJID_WINDOW, IID_IAccessible,
-                                   (void **) &retval);
-      return retval;
-    }
-  }
-
-  IAccessible *msaaAccessible;
-  aXPAccessible->GetNativeInterface((void**)&msaaAccessible);
-
+  IAccessible* msaaAccessible = nsnull;
+  aAccessible->GetNativeInterface(reinterpret_cast<void**>(&msaaAccessible));
   return static_cast<IDispatch*>(msaaAccessible);
 }
 
 void
 nsAccessibleWrap::UnattachIEnumVariant()
 {
   if (mEnumVARIANTPosition > 0)
     mEnumVARIANTPosition = kIEnumVariantDisconnected;
--- a/accessible/src/msaa/nsHTMLWin32ObjectAccessible.cpp
+++ b/accessible/src/msaa/nsHTMLWin32ObjectAccessible.cpp
@@ -109,16 +109,21 @@ nsLeafAccessible(nsnull, nsnull)
     // back to us and create another nsHTMLWin32ObjectAccessible
     HWND childWnd = ::GetWindow((HWND)aHwnd, GW_CHILD);
     if (childWnd) {
       mHwnd = childWnd;
     }
   }
 }
 
-NS_IMPL_ISUPPORTS_INHERITED1(nsHTMLWin32ObjectAccessible, nsAccessible, nsIAccessibleWin32Object)
+NS_IMPL_ISUPPORTS_INHERITED0(nsHTMLWin32ObjectAccessible, nsAccessible)
 
-NS_IMETHODIMP nsHTMLWin32ObjectAccessible::GetHwnd(void **aHwnd) 
+NS_IMETHODIMP 
+nsHTMLWin32ObjectAccessible::GetNativeInterface(void** aNativeAccessible)
 {
-  *aHwnd = mHwnd;
+  if (mHwnd) {
+    ::AccessibleObjectFromWindow(static_cast<HWND>(mHwnd),
+                                 OBJID_WINDOW, IID_IAccessible,
+                                 aNativeAccessible);
+  }
   return NS_OK;
 }
 
--- a/accessible/src/msaa/nsHTMLWin32ObjectAccessible.h
+++ b/accessible/src/msaa/nsHTMLWin32ObjectAccessible.h
@@ -34,17 +34,16 @@
  * 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 _nsHTMLWin32ObjectAccessible_H_
 #define _nsHTMLWin32ObjectAccessible_H_
 
-#include "nsIAccessibleWin32Object.h"
 #include "nsBaseWidgetAccessible.h"
 
 struct IAccessible;
 
 class nsHTMLWin32ObjectOwnerAccessible : public nsAccessibleWrap
 {
 public:
   // This will own the nsHTMLWin32ObjectAccessible. We create this where the
@@ -77,24 +76,24 @@ protected:
   * This class is used only internally, we never! send out an IAccessible linked
   *   back to this object. This class is used to represent a plugin object when
   *   referenced as a child or sibling of another nsAccessible node. We need only
   *   a limited portion of the nsIAccessible interface implemented here. The
   *   in depth accessible information will be returned by the actual IAccessible
   *   object returned by us in Accessible::NewAccessible() that gets the IAccessible
   *   from the windows system from the window handle.
   */
-class nsHTMLWin32ObjectAccessible : public nsLeafAccessible,
-                                    public nsIAccessibleWin32Object
+class nsHTMLWin32ObjectAccessible : public nsLeafAccessible
 {
 public:
 
   nsHTMLWin32ObjectAccessible(void *aHwnd);
   virtual ~nsHTMLWin32ObjectAccessible() {}
 
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIACCESSIBLEWIN32OBJECT
+
+  NS_IMETHOD GetNativeInterface(void** aNativeAccessible) MOZ_OVERRIDE;
 
 protected:
   void* mHwnd;
 };
 
 #endif  
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -278,37 +278,16 @@ function getTabDocAccessible(aAccOrElmOr
  */
 function getApplicationAccessible()
 {
   return gAccRetrieval.getApplicationAccessible().
     QueryInterface(nsIAccessibleApplication);
 }
 
 /**
- * Run through accessible tree of the given identifier so that we ensure
- * accessible tree is created.
- */
-function ensureAccessibleTree(aAccOrElmOrID)
-{
-  var acc = getAccessible(aAccOrElmOrID);
-  if (!acc)
-    return;
-
-  var child = acc.firstChild;
-  while (child) {
-    ensureAccessibleTree(child);
-    try {
-      child = child.nextSibling;
-    } catch (e) {
-      child = null;
-    }
-  }
-}
-
-/**
  * Compare expected and actual accessibles trees.
  *
  * @param  aAccOrElmOrID  [in] accessible identifier
  * @param  aAccTree       [in] JS object, each field corresponds to property of
  *                         accessible object. Additionally special properties
  *                         are presented:
  *                          children - an array of JS objects representing
  *                                      children of accessible
--- a/accessible/tests/mochitest/events/test_caretmove.html
+++ b/accessible/tests/mochitest/events/test_caretmove.html
@@ -41,31 +41,18 @@
     /**
      * Do tests.
      */
     var gQueue = null;
 
     // gA11yEventDumpID = "eventdump"; // debug stuff
     //gA11yEventDumpToConsole = true;
 
-    function testCaretOffset(aAccOrElmOrID, aCaretOffset)
-    {
-      var acc = getAccessible(aAccOrElmOrID, [nsIAccessibleText]);
-      is(acc.caretOffset, aCaretOffset,
-         "Wrong caret offset for " + aAccOrElmOrID);
-    }
-
     function doTests()
     {
-      todo(false, "enable commented tests Bug 510128 is fixed");
-      // test no focused accessibles
-      //testCaretOffset("textbox", -1);
-      //testCaretOffset("textarea", -1);
-      testCaretOffset("p", -1);
-
       // test caret move events and caret offsets
       gQueue = new eventQueue();
 
       var id = "textbox";
       gQueue.push(new synthFocus(id, new caretMoveChecker(5, id)));
       gQueue.push(new synthSelectAll(id, new caretMoveChecker(5, id)));
       gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
       gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
--- a/accessible/tests/mochitest/events/test_text.html
+++ b/accessible/tests/mochitest/events/test_text.html
@@ -46,17 +46,16 @@
      */
     function removeChildSpan(aID)
     {
       this.__proto__ = new textRemoveInvoker(aID, 0, 5, "33322");
 
       this.invoke = function removeChildSpan_invoke()
       {
         // remove HTML span, a first child of the node
-        ensureAccessibleTree(this.DOMNode);
         this.DOMNode.removeChild(this.DOMNode.firstChild);
       }
 
       this.getID = function removeChildSpan_getID()
       {
         return "Remove inaccessible span containing accessible nodes" + prettyName(aID);
       }
     }
@@ -153,18 +152,16 @@
      * versa.
      */
     function removeChildren(aID, aLastToFirst, aStart, aEnd, aText)
     {
       this.__proto__ = new textRemoveInvoker(aID, aStart, aEnd, aText);
 
       this.invoke = function removeChildren_invoke()
       {
-        ensureAccessibleTree(this.DOMNode);
-
         if (aLastToFirst) {
           while (this.DOMNode.firstChild)
             this.DOMNode.removeChild(this.DOMNode.lastChild);
         } else {
           while (this.DOMNode.firstChild)
             this.DOMNode.removeChild(this.DOMNode.firstChild);
         }
       }
--- a/accessible/tests/mochitest/events/test_tree.xul
+++ b/accessible/tests/mochitest/events/test_tree.xul
@@ -124,21 +124,16 @@
      */
     function setTreeView()
     {
       this.invoke = function setTreeView_invoke()
       {
         gTreeBox.view = gView;
       }
 
-      this.finalCheck = function setTreeView_finalCheck(aEvent)
-      {
-        ensureAccessibleTree(gTree);
-      }
-
       this.getID = function setTreeView_getID() { return "set tree view"; }
 
       this.eventSeq = [
         new treeViewChangedChecker(),
         new invokerChecker(EVENT_REORDER, gTree)
       ];
     };
 
@@ -169,21 +164,40 @@
     /**
      * Invalidates first column and checks six name changed events for each
      * treeitem plus TreeInvalidated event.
      */
     function invalidateColumn()
     {
       this.invoke = function invalidateColumn_invoke()
       {
-        // Make sure accessibles for the tree is created because it makes
-        // sure accessible events will be fired.
-        // Make sure tree children accessibles are created otherwise they won't
-        // be a cause of name changed events.
-        ensureAccessibleTree(gTree);
+        // Make sure accessible subtree of XUL tree is created otherwise no
+        // name change events for cell accessibles are emitted.
+        var tree = getAccessible(gTree);
+        var child = tree.firstChild;
+        var walkDown = true;
+        while (child != tree) {
+          if (walkDown) {
+            var grandChild = child.firstChild;
+            if (grandChild) {
+              child = grandChild;
+              continue;
+            }
+          }
+
+          var sibling = child.nextSibling;
+          if (sibling) {
+            child = sibling;
+            walkDown = true;
+            continue;
+          }
+
+          child = child.parent;
+          walkDown = false;
+        }
 
         // Fire 'TreeInvalidated' event by InvalidateColumn()
         var firstCol = gTree.columns.getFirstColumn();
         for (var i = 0; i < gView.rowCount; i++)
           gView.setCellText(i, firstCol, "hey " + String(i) + "x0");
 
         gTreeBox.invalidateColumn(firstCol);
       }
--- a/accessible/tests/mochitest/name/markup.js
+++ b/accessible/tests/mochitest/name/markup.js
@@ -117,18 +117,16 @@ function testNamesForMarkup(aMarkupElm)
   document.body.appendChild(div);
 }
 
 function testNamesForMarkupRules(aMarkupElm, aContainer)
 {
   if (gDumpToConsole)
     dump("\nProcessing markup rules '" + aMarkupElm.getAttribute("id") + "'\n");
 
-  ensureAccessibleTree(aContainer);
-
   var serializer = new XMLSerializer();
 
   var expr = "//html/body/div[@id='test']/" + aMarkupElm.getAttribute("ref");
   var elms = evaluateXPath(document, expr, htmlDocResolver);
 
   var ruleId = aMarkupElm.getAttribute("ruleset");
   var ruleElms = getRuleElmsByRulesetId(ruleId);
 
--- a/accessible/tests/mochitest/relations/test_embeds.xul
+++ b/accessible/tests/mochitest/relations/test_embeds.xul
@@ -16,54 +16,38 @@
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
           src="../states.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
   <script type="application/javascript"
           src="../relations.js"></script>
+  <script type="application/javascript"
+          src="../browser.js"></script>
 
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
-    // Helpers
-
-    function tabBrowser()
-    {
-      return gBrowserWnd.gBrowser;
-    }
-
-    function currentBrowser()
-    {
-      return tabBrowser().selectedBrowser;
-    }
-
-    function currentTabDocument()
-    {
-      return currentBrowser().contentDocument;
-    }
-
-    ////////////////////////////////////////////////////////////////////////////
     // Invokers
 
     function loadURI(aURI)
     {
       this.invoke = function loadURI_invoke()
       {
         tabBrowser().loadURI(aURI);
       }
 
       this.eventSeq = [
         new invokerChecker(EVENT_REORDER, currentBrowser)
       ];
 
       this.finalCheck = function loadURI_finalCheck()
       {
-        testRelation(gBrowserWnd.document, RELATION_EMBEDS,
+        testRelation(browserDocument(), RELATION_EMBEDS,
                      getAccessible(currentTabDocument()));
       }
 
       this.getID = function loadURI_getID()
       {
         return "load uri " + aURI;
       }
     }
@@ -76,67 +60,51 @@
       }
 
       this.eventSeq = [
         new invokerChecker(EVENT_REORDER, currentBrowser)
       ];
 
       this.finalCheck = function loadURI_finalCheck()
       {
-        testRelation(gBrowserWnd.document, RELATION_EMBEDS,
+        testRelation(browserDocument(), RELATION_EMBEDS,
                      getAccessible(currentTabDocument()));
       }
 
       this.getID = function loadOneTab_getID()
       {
         return "load uri '" + aURI + "' in new tab";
       }
     }
 
     ////////////////////////////////////////////////////////////////////////////
     // Testing
 
-    var gBrowserWnd = null;
-    function loadBrowser()
-    {
-      gBrowserWnd = window.openDialog("chrome://browser/content/", "_blank",
-                                      "chrome,all,dialog=no", "about:");
-
-      addA11yLoadEvent(startTests, gBrowserWnd);
-    }
-
-    function startTests()
-    {
-      // Wait for tab load.
-      var browser = currentBrowser();
-      addA11yLoadEvent(doTests, browser.contentWindow);
-    }
-
     //gA11yEventDumpToConsole = true; // debug
 
     var gQueue = null;
     function doTests()
     {
-      testRelation(gBrowserWnd.document, RELATION_EMBEDS,
+      testRelation(browserDocument(), RELATION_EMBEDS,
                    getAccessible(currentTabDocument()));
 
       gQueue = new eventQueue();
 
       gQueue.push(new loadURI("about:about"));
       gQueue.push(new loadOneTab("about:mozilla"));
 
       gQueue.onFinish = function()
       {
-        gBrowserWnd.close();
+        closeBrowserWindow();
       }
       gQueue.invoke();
     }
 
     SimpleTest.waitForExplicitFinish();
-    addLoadEvent(loadBrowser);
+    openBrowserWindow(doTests, "about:");
   ]]>
   </script>
 
   <vbox flex="1" style="overflow: auto;">
   <body xmlns="http://www.w3.org/1999/xhtml">
     <a target="_blank"
        href="https://bugzilla.mozilla.org/show_bug.cgi?id=707654"
        title="Embeds relation on root accessible can return not content document">
--- a/accessible/tests/mochitest/textcaret/test_general.html
+++ b/accessible/tests/mochitest/textcaret/test_general.html
@@ -21,27 +21,43 @@
     function turnCaretBrowsing(aIsOn)
     {
       var prefs = Components.classes["@mozilla.org/preferences-service;1"].
         getService(Components.interfaces.nsIPrefBranch);
       prefs.setBoolPref("accessibility.browsewithcaret", aIsOn);
     }
 
     /**
+     * Test caret offset for the given accessible.
+     */
+    function testCaretOffset(aID, aCaretOffset)
+    {
+      var acc = getAccessible(aID, [nsIAccessibleText]);
+      is(acc.caretOffset, aCaretOffset,
+         "Wrong caret offset for " + aID);
+    }
+
+    /**
      * Do tests.
      */
     var gQueue = null;
 
     //gA11yEventDumpID = "eventdump"; // debug stuff
     //gA11yEventDumpToConsole = true;
 
     function doTests()
     {
       turnCaretBrowsing(true);
 
+      // test caret offsets
+      testCaretOffset(document, 14);
+      testCaretOffset("textbox", -1);
+      testCaretOffset("textarea", -1);
+      testCaretOffset("p", -1);
+
       // test caret move events and caret offsets
       gQueue = new eventQueue();
 
       gQueue.push(new setCaretOffset("textbox", 1, "textbox"));
       gQueue.push(new setCaretOffset("link", 1, "link"));
       gQueue.push(new setCaretOffset("heading", 1, document));
       gQueue.onFinish = function()
       {
@@ -54,29 +70,41 @@
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
 <body>
 
   <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=448744"
+     title="caretOffset should return -1 if the system caret is not currently with in that particular object">
+    Mozilla Bug 448744
+  </a>
+  <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=524115"
      title="HyperText accessible should get focus when the caret is positioned inside of it, text is changed or copied into clipboard by ATs">
     Mozilla Bug 524115
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=546068"
      title="Position is not being updated when atk_text_set_caret_offset is used">
     Mozilla Bug 546068
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=725581"
+     title="caretOffset for textarea should be -1 when textarea doesn't have a focus">
+    Mozilla Bug 725581
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <input id="textbox" value="hello"/>
+  <textarea id="textarea">text<br>text</textarea>
+  <p id="p" contentEditable="true"><span>text</span><br/>text</p>
   <a id="link" href="about:">about mozilla</a>
   <h5 id="heading">heading</h5>
 
   <div id="eventdump"></div>
 </body>
 </html>
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -423,8 +423,10 @@ pref("media.realtime_decoder.enabled", t
 // our handling of requestAnimationFrame() listeners, which are
 // supposed to enable this REPEATING_PRECISE_CAN_SKIP behavior.  The
 // secondary bug isn't really worth investigating since it's obseleted
 // by bug 710563.
 pref("layout.frame_rate.precise", true);
 
 // Screen timeout in minutes
 pref("power.screen.timeout", 60);
+
+pref("full-screen-api.enabled", true);
--- a/b2g/chrome/content/webapi.js
+++ b/b2g/chrome/content/webapi.js
@@ -15,38 +15,20 @@ Cu.import('resource://gre/modules/Servic
 XPCOMUtils.defineLazyGetter(Services, 'fm', function() {
   return Cc['@mozilla.org/focus-manager;1']
            .getService(Ci.nsIFocusManager);
 });
 
 (function() {
   function generateAPI(window) {
     let navigator = window.navigator;
-    XPCOMUtils.defineLazyGetter(navigator, 'mozSettings', function() {
-      return new Settings();
-    });
-
-    XPCOMUtils.defineLazyGetter(navigator, 'mozContacts', function() {
-      return ContactsManager;
-    });
-
-    XPCOMUtils.defineLazyGetter(navigator, 'mozApps', function() {
-      let mozApps = {
-        enumerate: function mozAppsEnumerate(callback) {
-          callback(webapps);
-        }
-      };
-      return mozApps;
-    });
 
     XPCOMUtils.defineLazyGetter(navigator, 'mozKeyboard', function() {
       return new MozKeyboard();
     });
-
-    updateApplicationCache(window);
   };
 
   let progressListener = {
     onStateChange: function onStateChange(progress, request,
                                           flags, status) {
     },
 
     onProgressChange: function onProgressChange(progress, request,
@@ -86,32 +68,16 @@ XPCOMUtils.defineLazyGetter(Services, 'f
 
   let flags = Ci.nsIWebProgress.NOTIFY_LOCATION;
   let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIWebProgress);
   flags = Ci.nsIWebProgress.NOTIFY_ALL;
   webProgress.addProgressListener(progressListener, flags);
 })();
 
-
-// WebApps - Application Cache
-function updateApplicationCache(window) {
-  try {
-    var cache = window.applicationCache;
-    cache.update();
-
-    cache.addEventListener('updateready', function updateReady(evt) {
-      // XXX Add a nice UI when an update is ready asking if the user
-      // want to reload the application now.
-      cache.swapCache();
-      window.document.location.reload();
-    });
-  } catch (e) {}
-}
-
 // MozKeyboard
 (function VirtualKeyboardManager() {
   let activeElement = null;
   let isKeyboardOpened = false;
   
   function fireEvent(type, details) {
     let event = content.document.createEvent('CustomEvent');
     event.initCustomEvent(type, true, true, details ? details : {});
@@ -175,1346 +141,16 @@ MozKeyboard.prototype = {
     let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDOMWindowUtils);
     ['keydown', 'keypress', 'keyup'].forEach(function sendKey(type) {
       utils.sendKeyEvent(type, keyCode, charCode, null);
     });
   }
 };
 
-// MozSettings - Bug 678695
-function Settings() {
-  content.addEventListener('message', this);
-}
-
-Settings.prototype = {
-  ERROR_SETTING_UNKNOWN: 0x0001,
-
-  _requests: [],
-  handleEvent: function settings_handleEvent(event) {
-    var data = event.data;
-    if (typeof data !== 'string')
-      return;
-
-    var cmd = data.split(':');
-    if (cmd.length < 4 || cmd[0] != 'settings')
-      return;
-
-    var method = cmd[1], id = cmd[2], key = cmd[3], value = cmd[4];
-    var request = this._requests[id];
-    switch (method) {
-      case 'error':
-        request.error = this.ERROR_SETTING_UNKNOWN;
-        this._dispatchEvent(request, request.TYPE_ERROR);
-        break;
-
-      case 'success':
-        request.result = new SettingsMessage(key, value);
-        this._dispatchEvent(request, request.TYPE_SUCCESS);
-        break;
-    }
-  },
-
-  get: function settings_get(key) {
-    var request = new SettingsRequest();
-    var id = this._requests.length;
-    this._requests.push(request);
-
-    var msg = 'settings:get:' + id + ':' + key;
-    content.top.wrappedJSObject.postMessage(msg, '*');
-    return request;
-  },
-
-  set: function settings_set(key, value) {
-    var request = new SettingsRequest();
-    var id = this._requests.length;
-    this._requests.push(request);
-
-    var msg = 'settings:set:' + id + ':' + key + ':' + value;
-    content.top.wrappedJSObject.postMessage(msg, '*');
-    return request;
-  },
-
-  _dispatchEvent: function(target, type) {
-    var evt = content.document.createEvent('CustomEvent');
-    evt.initCustomEvent(type, true, false, null);
-    target.dispatchEvent(evt);
-  }
-};
-
-
-/* ========== nsIDOMMozSettingsRequest ========== */
-function SettingsRequest() {
-  this.readyState = this.STATE_PROCESSING;
-
-  this.error = null;
-  this.onerror = null;
-  // XXX should be an array
-  this._errorCallback = null;
-
-  this.result = null;
-  this.onsuccess = null;
-  // XXX should be an array
-  this._successCallback = null;
-}
-
-SettingsRequest.prototype = {
-  // States of the request
-  STATE_PROCESSING: 'processing',
-  STATE_DONE: 'done',
-
-  // Types of events
-  TYPE_SUCCESS: 'success',
-  TYPE_ERROR: 'error',
-
-  addEventListener: function sr_addEventListener(type, callback) {
-    switch (type) {
-      case this.TYPE_SUCCESS:
-        this._successCallback = callback;
-        break;
-      case this.TYPE_ERROR:
-        this._errorCallback = callback;
-        break;
-    }
-  },
-
-  removeEventListener: function sr_removeEventListener(type, callback) {
-    switch (type) {
-      case this.TYPE_SUCCESS:
-        this._successCallback = null;
-        break;
-      case this.TYPE_ERROR:
-        this._errorCallback = null;
-        break;
-    }
-  },
-
-  dispatchEvent: function sr_dispatchEvent(event) {
-    this.readyState = this.STATE_DONE;
-
-    switch (event.type) {
-      case this.TYPE_SUCCESS:
-        if (this._successCallback)
-          this._successCallback(event);
-
-        if (this.onsuccess)
-          this.onsuccess(event);
-        break;
-      case this.TYPE_ERROR:
-        if (this._errorCallback)
-          this._errorCallback(event);
-
-        if (this.onerror)
-          this.onerror(event);
-        break;
-    }
-  }
-};
-
-/* ========== nsIDOMMozSettingsMessage ========== */
-function SettingsMessage(name, value) {
-  this.name = name;
-  this.value = value;
-}
-
-
-// MozApps - Bug 709015
-var webapps = [
-  { // clock 
-    installOrigin: 'http://gaiamobile.org:8888',
-    origin: '../clock',
-    receipt: null,
-    installTime: 1323339869000,
-    manifest: {
-      'name': 'Clock',
-      'description': 'Gaia Clock',
-      'launch_path': '/clock.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Clock.png'
-      }
-    }
-  },
-  { // browser
-    installOrigin: 'http://gaiamobile.org:8888',
-    origin: '../browser',
-    receipt: null,
-    installTime: 1323339869000,
-    manifest: {
-      'name': 'Browser',
-      'description': 'Gaia Web Browser',
-      'launch_path': '/browser.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Browser.png'
-      }
-    }
-  },
-  { // camera
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../camera',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Camera',
-      'description': 'Gaia Camera',
-      'launch_path': '/camera.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Camera.png'
-      }
-    }
-  },
-  { // dialer
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../dialer',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Dialer',
-      'description': 'Gaia Dialer',
-      'launch_path': '/dialer.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Phone.png'
-      }
-    }
-  },
-  { // gallery
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../gallery',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Gallery',
-      'description': 'Gaia Gallery',
-      'launch_path': '/gallery.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Gallery.png'
-      }
-    }
-  },
-  { // music
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../music',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Music',
-      'description': 'Gaia Music',
-      'launch_path': '/music.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Music.png'
-      }
-    }
-  },
-  { // market
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../market',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Market',
-      'description': 'Market for downloading and installing apps',
-      'launch_path': '/market.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Market.png'
-      }
-    }
-  },
-  { // settings
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../settings',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Settings',
-      'description': 'Gaia Settings',
-      'launch_path': '/settings.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Settings.png'
-      }
-    }
-  },
-  { // sms
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../sms',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Messages',
-      'description': 'Gaia Messages',
-      'launch_path': '/sms.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Messages.png'
-      }
-    }
-  }
-];
-
-
-// Bug 674720 - webContacts
-var contacts = [
-  {
-    id: '3',
-    displayName: 'Coby Newman',
-    name: {
-      familyName: ['Coby'],
-      givenName: ['Newman']
-    },
-    phones: ['1-823-949-7735'],
-    emails: ['posuere.at@hendreritaarcu.com']
-  },
-  {
-    id: '6',
-    displayName: 'Caesar Velasquez',
-    name: {
-      familyName: ['Caesar'],
-      givenName: ['Velasquez']
-    },
-    phones: ['1-355-185-5419'],
-    emails: ['fames@Duis.org']
-  },
-  {
-    id: '9',
-    displayName: 'Hamilton Farrell',
-    name: {
-      familyName: ['Hamilton'],
-      givenName: ['Farrell']
-    },
-    phones: ['1-682-456-9186'],
-    emails: ['sem@Uttinciduntvehicula.com']
-  },
-  {
-    id: '12',
-    displayName: 'Emery Livingston',
-    name: {
-      familyName: ['Emery'],
-      givenName: ['Livingston']
-    },
-    phones: ['1-510-151-9801'],
-    emails: ['orci.luctus.et@massaInteger.com']
-  },
-  {
-    id: '15',
-    displayName: 'Griffith Heath',
-    name: {
-      familyName: ['Griffith'],
-      givenName: ['Heath']
-    },
-    phones: ['1-800-719-3201'],
-    emails: ['dapibus@Inlorem.ca']
-  },
-  {
-    id: '18',
-    displayName: 'Luke Stuart',
-    name: {
-      familyName: ['Luke'],
-      givenName: ['Stuart']
-    },
-    phones: ['1-120-910-1976'],
-    emails: ['congue@nibh.ca']
-  },
-  {
-    id: '21',
-    displayName: 'Brennan Love',
-    name: {
-      familyName: ['Brennan'],
-      givenName: ['Love']
-    },
-    phones: ['1-724-155-2807'],
-    emails: ['interdum.libero.dui@cursusvestibulum.edu']
-  },
-  {
-    id: '24',
-    displayName: 'Lamar Meadows',
-    name: {
-      familyName: ['Lamar'],
-      givenName: ['Meadows']
-    },
-    phones: ['1-976-164-8769'],
-    emails: ['tincidunt@non.com']
-  },
-  {
-    id: '27',
-    displayName: 'Erasmus Flynn',
-    name: {
-      familyName: ['Erasmus'],
-      givenName: ['Flynn']
-    },
-    phones: ['1-488-678-3487'],
-    emails: ['lorem.ut.aliquam@eu.ca']
-  },
-  {
-    id: '30',
-    displayName: 'Aladdin Ellison',
-    name: {
-      familyName: ['Aladdin'],
-      givenName: ['Ellison']
-    },
-    phones: ['1-977-743-6797'],
-    emails: ['sociosqu.ad@sollicitudin.org']
-  },
-  {
-    id: '33',
-    displayName: 'Valentine Rasmussen',
-    name: {
-      familyName: ['Valentine'],
-      givenName: ['Rasmussen']
-    },
-    phones: ['1-265-504-2025'],
-    emails: ['ultrices.iaculis@acsem.edu']
-  },
-  {
-    id: '36',
-    displayName: 'Deacon Murphy',
-    name: {
-      familyName: ['Deacon'],
-      givenName: ['Murphy']
-    },
-    phones: ['1-770-450-1221'],
-    emails: ['varius@erat.edu']
-  },
-  {
-    id: '39',
-    displayName: 'Paul Kennedy',
-    name: {
-      familyName: ['Paul'],
-      givenName: ['Kennedy']
-    },
-    phones: ['1-689-891-3529'],
-    emails: ['ac.arcu@vitae.edu']
-  },
-  {
-    id: '42',
-    displayName: 'Aaron Chase',
-    name: {
-      familyName: ['Aaron'],
-      givenName: ['Chase']
-    },
-    phones: ['1-451-574-7937'],
-    emails: ['tempor.bibendum.Donec@pharetraQuisque.edu']
-  },
-  {
-    id: '45',
-    displayName: 'Geoffrey Dunn',
-    name: {
-      familyName: ['Geoffrey'],
-      givenName: ['Dunn']
-    },
-    phones: ['1-924-387-2395'],
-    emails: ['a.malesuada@tellusPhasellus.com']
-  },
-  {
-    id: '48',
-    displayName: 'Ashton Russo',
-    name: {
-      familyName: ['Ashton'],
-      givenName: ['Russo']
-    },
-    phones: ['1-182-776-5600'],
-    emails: ['Aliquam.vulputate.ullamcorper@faucibusorci.edu']
-  },
-  {
-    id: '51',
-    displayName: 'Owen Noble',
-    name: {
-      familyName: ['Owen'],
-      givenName: ['Noble']
-    },
-    phones: ['1-463-693-1336'],
-    emails: ['et@vulputateveliteu.ca']
-  },
-  {
-    id: '54',
-    displayName: 'Kamal Blake',
-    name: {
-      familyName: ['Kamal'],
-      givenName: ['Blake']
-    },
-    phones: ['1-636-197-1985'],
-    emails: ['tempor@malesuada.edu']
-  },
-  {
-    id: '57',
-    displayName: 'Tyrone Delaney',
-    name: {
-      familyName: ['Tyrone'],
-      givenName: ['Delaney']
-    },
-    phones: ['1-886-920-6283'],
-    emails: ['est@aliquetsemut.com']
-  },
-  {
-    id: '60',
-    displayName: 'Ciaran Sellers',
-    name: {
-      familyName: ['Ciaran'],
-      givenName: ['Sellers']
-    },
-    phones: ['1-315-414-0323'],
-    emails: ['Etiam@Nulla.com']
-  },
-  {
-    id: '63',
-    displayName: 'Bernard Alford',
-    name: {
-      familyName: ['Bernard'],
-      givenName: ['Alford']
-    },
-    phones: ['1-430-958-2651'],
-    emails: ['elementum.lorem.ut@sociisnatoque.edu']
-  },
-  {
-    id: '66',
-    displayName: 'Kamal Cote',
-    name: {
-      familyName: ['Kamal'],
-      givenName: ['Cote']
-    },
-    phones: ['1-666-609-9141'],
-    emails: ['eleifend.egestas@cursus.edu']
-  },
-  {
-    id: '69',
-    displayName: 'Lucius Mckee',
-    name: {
-      familyName: ['Lucius'],
-      givenName: ['Mckee']
-    },
-    phones: ['1-224-590-6780'],
-    emails: ['Fusce.dolor@tellusnon.org']
-  },
-  {
-    id: '72',
-    displayName: 'Dale Coleman',
-    name: {
-      familyName: ['Dale'],
-      givenName: ['Coleman']
-    },
-    phones: ['1-320-245-3036'],
-    emails: ['dapibus.rutrum@ametlorem.org']
-  },
-  {
-    id: '75',
-    displayName: 'Kermit Nguyen',
-    name: {
-      familyName: ['Kermit'],
-      givenName: ['Nguyen']
-    },
-    phones: ['1-247-825-8563'],
-    emails: ['per@risusMorbi.org']
-  },
-  {
-    id: '78',
-    displayName: 'Timon Horton',
-    name: {
-      familyName: ['Timon'],
-      givenName: ['Horton']
-    },
-    phones: ['1-739-233-8981'],
-    emails: ['Etiam@nonummyultriciesornare.ca']
-  },
-  {
-    id: '81',
-    displayName: 'Dale Lamb',
-    name: {
-      familyName: ['Dale'],
-      givenName: ['Lamb']
-    },
-    phones: ['1-640-507-8295'],
-    emails: ['dapibus.id@pedeac.edu']
-  },
-  {
-    id: '84',
-    displayName: 'Owen Acevedo',
-    name: {
-      familyName: ['Owen'],
-      givenName: ['Acevedo']
-    },
-    phones: ['1-403-201-3170'],
-    emails: ['porttitor.tellus.non@dolorFusce.edu']
-  },
-  {
-    id: '87',
-    displayName: 'Richard Mckee',
-    name: {
-      familyName: ['Richard'],
-      givenName: ['Mckee']
-    },
-    phones: ['1-783-513-0684'],
-    emails: ['senectus.et.netus@Vestibulum.com']
-  },
-  {
-    id: '90',
-    displayName: 'Elijah Bass',
-    name: {
-      familyName: ['Elijah'],
-      givenName: ['Bass']
-    },
-    phones: ['1-632-950-0553'],
-    emails: ['erat@sapien.com']
-  },
-  {
-    id: '93',
-    displayName: 'Barrett Wells',
-    name: {
-      familyName: ['Barrett'],
-      givenName: ['Wells']
-    },
-    phones: ['1-112-180-5617'],
-    emails: ['interdum.ligula@varius.edu']
-  },
-  {
-    id: '96',
-    displayName: 'Herman Meyer',
-    name: {
-      familyName: ['Herman'],
-      givenName: ['Meyer']
-    },
-    phones: ['1-296-252-5507'],
-    emails: ['urna@vitaealiquameros.org']
-  },
-  {
-    id: '99',
-    displayName: 'Ashton Hinton',
-    name: {
-      familyName: ['Ashton'],
-      givenName: ['Hinton']
-    },
-    phones: ['1-695-256-8929'],
-    emails: ['lorem@mattisornare.org']
-  },
-  {
-    id: '102',
-    displayName: 'Harrison Marsh',
-    name: {
-      familyName: ['Harrison'],
-      givenName: ['Marsh']
-    },
-    phones: ['1-897-458-1730'],
-    emails: ['pharetra.felis.eget@auctor.com']
-  },
-  {
-    id: '105',
-    displayName: 'Benedict Santana',
-    name: {
-      familyName: ['Benedict'],
-      givenName: ['Santana']
-    },
-    phones: ['1-565-457-4828'],
-    emails: ['amet.metus.Aliquam@Maecenas.org']
-  },
-  {
-    id: '108',
-    displayName: 'David Church',
-    name: {
-      familyName: ['David'],
-      givenName: ['Church']
-    },
-    phones: ['1-179-353-3314'],
-    emails: ['Nullam.enim@Utsagittis.edu']
-  },
-  {
-    id: '111',
-    displayName: 'Colt Wolfe',
-    name: {
-      familyName: ['Colt'],
-      givenName: ['Wolfe']
-    },
-    phones: ['1-587-970-8581'],
-    emails: ['hendrerit.Donec.porttitor@tinciduntaliquam.org']
-  },
-  {
-    id: '114',
-    displayName: 'Carlos Bishop',
-    name: {
-      familyName: ['Carlos'],
-      givenName: ['Bishop']
-    },
-    phones: ['1-963-305-6702'],
-    emails: ['Nam@cursusNunc.org']
-  },
-  {
-    id: '117',
-    displayName: 'Dominic Ware',
-    name: {
-      familyName: ['Dominic'],
-      givenName: ['Ware']
-    },
-    phones: ['1-609-458-5449'],
-    emails: ['Fusce.aliquet@Etiam.ca']
-  },
-  {
-    id: '120',
-    displayName: 'Phillip Whitley',
-    name: {
-      familyName: ['Phillip'],
-      givenName: ['Whitley']
-    },
-    phones: ['1-284-955-1766'],
-    emails: ['per.inceptos.hymenaeos@nequesedsem.ca']
-  },
-  {
-    id: '123',
-    displayName: 'Valentine Sargent',
-    name: {
-      familyName: ['Valentine'],
-      givenName: ['Sargent']
-    },
-    phones: ['1-346-890-6417'],
-    emails: ['nec@dolorFusce.com']
-  },
-  {
-    id: '126',
-    displayName: 'Gabriel Huber',
-    name: {
-      familyName: ['Gabriel'],
-      givenName: ['Huber']
-    },
-    phones: ['1-399-465-0589'],
-    emails: ['pretium.neque@nislsemconsequat.ca']
-  },
-  {
-    id: '129',
-    displayName: 'George Tyler',
-    name: {
-      familyName: ['George'],
-      givenName: ['Tyler']
-    },
-    phones: ['1-739-571-2737'],
-    emails: ['blandit.viverra.Donec@dictum.ca']
-  },
-  {
-    id: '132',
-    displayName: 'Asher Carey',
-    name: {
-      familyName: ['Asher'],
-      givenName: ['Carey']
-    },
-    phones: ['1-477-425-4723'],
-    emails: ['torquent.per.conubia@blanditNamnulla.edu']
-  },
-  {
-    id: '135',
-    displayName: 'Anthony Solomon',
-    name: {
-      familyName: ['Anthony'],
-      givenName: ['Solomon']
-    },
-    phones: ['1-570-753-4296'],
-    emails: ['risus.Nunc@hendreritconsectetuercursus.com']
-  },
-  {
-    id: '138',
-    displayName: 'Griffith Fuller',
-    name: {
-      familyName: ['Griffith'],
-      givenName: ['Fuller']
-    },
-    phones: ['1-779-242-5342'],
-    emails: ['Suspendisse@aliquam.ca']
-  },
-  {
-    id: '141',
-    displayName: 'Beau Brewer',
-    name: {
-      familyName: ['Beau'],
-      givenName: ['Brewer']
-    },
-    phones: ['1-664-184-7334'],
-    emails: ['magna.tellus.faucibus@ultricesposuerecubilia.com']
-  },
-  {
-    id: '144',
-    displayName: 'Jordan Campbell',
-    name: {
-      familyName: ['Jordan'],
-      givenName: ['Campbell']
-    },
-    phones: ['1-593-938-2525'],
-    emails: ['Curae;.Phasellus@Morbiquis.ca']
-  },
-  {
-    id: '147',
-    displayName: 'Cyrus Cabrera',
-    name: {
-      familyName: ['Cyrus'],
-      givenName: ['Cabrera']
-    },
-    phones: ['1-915-748-1349'],
-    emails: ['lorem.tristique@acmetus.edu']
-  },
-  {
-    id: '150',
-    displayName: 'Hamilton Boone',
-    name: {
-      familyName: ['Hamilton'],
-      givenName: ['Boone']
-    },
-    phones: ['1-278-421-9845'],
-    emails: ['non.sapien@quamdignissimpharetra.edu']
-  },
-  {
-    id: '153',
-    displayName: 'Wallace Donovan',
-    name: {
-      familyName: ['Wallace'],
-      givenName: ['Donovan']
-    },
-    phones: ['1-940-175-9334'],
-    emails: ['justo@lacusMaurisnon.org']
-  },
-  {
-    id: '156',
-    displayName: 'Kirk Buckley',
-    name: {
-      familyName: ['Kirk'],
-      givenName: ['Buckley']
-    },
-    phones: ['1-283-177-6304'],
-    emails: ['Cras@Morbinon.edu']
-  },
-  {
-    id: '159',
-    displayName: 'Simon Hall',
-    name: {
-      familyName: ['Simon'],
-      givenName: ['Hall']
-    },
-    phones: ['1-269-202-5174'],
-    emails: ['mus.Proin@dolor.org']
-  },
-  {
-    id: '162',
-    displayName: 'Trevor Rush',
-    name: {
-      familyName: ['Trevor'],
-      givenName: ['Rush']
-    },
-    phones: ['1-865-595-9074'],
-    emails: ['Fusce@Donec.edu']
-  },
-  {
-    id: '165',
-    displayName: 'Todd Mccormick',
-    name: {
-      familyName: ['Todd'],
-      givenName: ['Mccormick']
-    },
-    phones: ['1-398-916-3514'],
-    emails: ['at@ornareelit.org']
-  },
-  {
-    id: '168',
-    displayName: 'Yuli Gay',
-    name: {
-      familyName: ['Yuli'],
-      givenName: ['Gay']
-    },
-    phones: ['1-198-196-4256'],
-    emails: ['Sed.congue.elit@Inornare.edu']
-  },
-  {
-    id: '171',
-    displayName: 'Joseph Frazier',
-    name: {
-      familyName: ['Joseph'],
-      givenName: ['Frazier']
-    },
-    phones: ['1-969-410-7180'],
-    emails: ['faucibus.ut.nulla@massa.org']
-  },
-  {
-    id: '174',
-    displayName: 'Ali Chase',
-    name: {
-      familyName: ['Ali'],
-      givenName: ['Chase']
-    },
-    phones: ['1-598-924-6112'],
-    emails: ['eu.elit@necanteMaecenas.edu']
-  },
-  {
-    id: '177',
-    displayName: 'Guy Simpson',
-    name: {
-      familyName: ['Guy'],
-      givenName: ['Simpson']
-    },
-    phones: ['1-558-377-3714'],
-    emails: ['in@mauriselit.edu']
-  },
-  {
-    id: '180',
-    displayName: 'Ivan Wynn',
-    name: {
-      familyName: ['Ivan'],
-      givenName: ['Wynn']
-    },
-    phones: ['1-274-885-0477'],
-    emails: ['lobortis.quis@Sed.com']
-  },
-  {
-    id: '183',
-    displayName: 'Preston Carpenter',
-    name: {
-      familyName: ['Preston'],
-      givenName: ['Carpenter']
-    },
-    phones: ['1-758-120-5270'],
-    emails: ['elit.Curabitur@vehiculaaliquet.edu']
-  },
-  {
-    id: '186',
-    displayName: 'Demetrius Santos',
-    name: {
-      familyName: ['Demetrius'],
-      givenName: ['Santos']
-    },
-    phones: ['1-913-961-7009'],
-    emails: ['id@magnaPhasellusdolor.com']
-  },
-  {
-    id: '189',
-    displayName: 'Dale Franklin',
-    name: {
-      familyName: ['Dale'],
-      givenName: ['Franklin']
-    },
-    phones: ['1-443-971-0116'],
-    emails: ['velit.Pellentesque@IntegerurnaVivamus.com']
-  },
-  {
-    id: '192',
-    displayName: 'Abraham Randolph',
-    name: {
-      familyName: ['Abraham'],
-      givenName: ['Randolph']
-    },
-    phones: ['1-368-169-0957'],
-    emails: ['egestas@maurisidsapien.com']
-  },
-  {
-    id: '195',
-    displayName: 'Hu Avila',
-    name: {
-      familyName: ['Hu'],
-      givenName: ['Avila']
-    },
-    phones: ['1-311-333-8877'],
-    emails: ['metus@adipiscinglacusUt.com']
-  },
-  {
-    id: '198',
-    displayName: 'Garth Trujillo',
-    name: {
-      familyName: ['Garth'],
-      givenName: ['Trujillo']
-    },
-    phones: ['1-409-494-1231'],
-    emails: ['commodo.hendrerit.Donec@etnunc.ca']
-  },
-  {
-    id: '201',
-    displayName: 'Quamar Buchanan',
-    name: {
-      familyName: ['Quamar'],
-      givenName: ['Buchanan']
-    },
-    phones: ['1-114-992-7225'],
-    emails: ['tellus@consequatpurusMaecenas.ca']
-  },
-  {
-    id: '204',
-    displayName: 'Ulysses Bishop',
-    name: {
-      familyName: ['Ulysses'],
-      givenName: ['Bishop']
-    },
-    phones: ['1-485-518-5941'],
-    emails: ['fermentum.fermentum.arcu@amalesuadaid.com']
-  },
-  {
-    id: '207',
-    displayName: 'Avram Knapp',
-    name: {
-      familyName: ['Avram'],
-      givenName: ['Knapp']
-    },
-    phones: ['1-307-139-5554'],
-    emails: ['est.ac.mattis@ultricesmauris.ca']
-  },
-  {
-    id: '210',
-    displayName: 'Conan Grant',
-    name: {
-      familyName: ['Conan'],
-      givenName: ['Grant']
-    },
-    phones: ['1-331-936-0280'],
-    emails: ['turpis@odio.com']
-  },
-  {
-    id: '213',
-    displayName: 'Chester Kemp',
-    name: {
-      familyName: ['Chester'],
-      givenName: ['Kemp']
-    },
-    phones: ['1-554-119-4848'],
-    emails: ['Aenean.gravida.nunc@eu.org']
-  },
-  {
-    id: '216',
-    displayName: 'Hedley Dudley',
-    name: {
-      familyName: ['Hedley'],
-      givenName: ['Dudley']
-    },
-    phones: ['1-578-607-6287'],
-    emails: ['Nunc@dignissimtemporarcu.ca']
-  },
-  {
-    id: '219',
-    displayName: 'Jermaine Avila',
-    name: {
-      familyName: ['Jermaine'],
-      givenName: ['Avila']
-    },
-    phones: ['1-860-455-2283'],
-    emails: ['accumsan@ametdapibusid.ca']
-  },
-  {
-    id: '222',
-    displayName: 'Kamal Hamilton',
-    name: {
-      familyName: ['Kamal'],
-      givenName: ['Hamilton']
-    },
-    phones: ['1-650-389-0920'],
-    emails: ['Fusce.dolor@nuncsed.ca']
-  },
-  {
-    id: '225',
-    displayName: 'Castor Maxwell',
-    name: {
-      familyName: ['Castor'],
-      givenName: ['Maxwell']
-    },
-    phones: ['1-260-489-7135'],
-    emails: ['diam.lorem@a.ca']
-  },
-  {
-    id: '228',
-    displayName: 'Lyle Burris',
-    name: {
-      familyName: ['Lyle'],
-      givenName: ['Burris']
-    },
-    phones: ['1-250-343-2038'],
-    emails: ['eget.lacus@tempordiamdictum.com']
-  },
-  {
-    id: '231',
-    displayName: 'Merrill Dalton',
-    name: {
-      familyName: ['Merrill'],
-      givenName: ['Dalton']
-    },
-    phones: ['1-851-675-1381'],
-    emails: ['eu.tempor@blanditmattisCras.edu']
-  },
-  {
-    id: '234',
-    displayName: 'Ezekiel Medina',
-    name: {
-      familyName: ['Ezekiel'],
-      givenName: ['Medina']
-    },
-    phones: ['1-389-582-3443'],
-    emails: ['lectus.sit@interdum.ca']
-  },
-  {
-    id: '237',
-    displayName: 'Len Tran',
-    name: {
-      familyName: ['Len'],
-      givenName: ['Tran']
-    },
-    phones: ['1-434-573-6114'],
-    emails: ['turpis.Aliquam.adipiscing@montesnasceturridiculus.com']
-  },
-  {
-    id: '240',
-    displayName: 'Len Dominguez',
-    name: {
-      familyName: ['Len'],
-      givenName: ['Dominguez']
-    },
-    phones: ['1-144-489-7487'],
-    emails: ['augue@Innec.ca']
-  },
-  {
-    id: '243',
-    displayName: 'Paul Lane',
-    name: {
-      familyName: ['Paul'],
-      givenName: ['Lane']
-    },
-    phones: ['1-448-169-4312'],
-    emails: ['lectus.Cum.sociis@dolornonummyac.org']
-  },
-  {
-    id: '246',
-    displayName: 'Eric Horne',
-    name: {
-      familyName: ['Eric'],
-      givenName: ['Horne']
-    },
-    phones: ['1-124-862-6890'],
-    emails: ['commodo.tincidunt.nibh@eleifendnuncrisus.com']
-  },
-  {
-    id: '249',
-    displayName: 'Elton Ellis',
-    name: {
-      familyName: ['Elton'],
-      givenName: ['Ellis']
-    },
-    phones: ['1-492-834-0019'],
-    emails: ['lorem.eu.metus@felis.ca']
-  },
-  {
-    id: '252',
-    displayName: 'Jameson Snyder',
-    name: {
-      familyName: ['Jameson'],
-      givenName: ['Snyder']
-    },
-    phones: ['1-811-590-5893'],
-    emails: ['fermentum@Nuncmaurissapien.org']
-  },
-  {
-    id: '255',
-    displayName: 'Micah Shelton',
-    name: {
-      familyName: ['Micah'],
-      givenName: ['Shelton']
-    },
-    phones: ['1-402-504-4026'],
-    emails: ['Nunc.mauris@malesuada.ca']
-  },
-  {
-    id: '258',
-    displayName: 'Evan Lester',
-    name: {
-      familyName: ['Evan'],
-      givenName: ['Lester']
-    },
-    phones: ['1-535-915-3570'],
-    emails: ['libero@adipiscingfringillaporttitor.org']
-  },
-  {
-    id: '261',
-    displayName: 'Reuben Dalton',
-    name: {
-      familyName: ['Reuben'],
-      givenName: ['Dalton']
-    },
-    phones: ['1-296-598-2504'],
-    emails: ['tincidunt.vehicula.risus@Craseutellus.com']
-  },
-  {
-    id: '264',
-    displayName: 'Beau Baird',
-    name: {
-      familyName: ['Beau'],
-      givenName: ['Baird']
-    },
-    phones: ['1-525-882-9957'],
-    emails: ['urna.suscipit.nonummy@facilisisvitae.com']
-  },
-  {
-    id: '267',
-    displayName: 'Hedley Olsen',
-    name: {
-      familyName: ['Hedley'],
-      givenName: ['Olsen']
-    },
-    phones: ['1-945-295-5863'],
-    emails: ['vulputate.ullamcorper@Vivamusnisi.org']
-  },
-  {
-    id: '270',
-    displayName: 'Oliver Todd',
-    name: {
-      familyName: ['Oliver'],
-      givenName: ['Todd']
-    },
-    phones: ['1-551-447-1296'],
-    emails: ['Donec.egestas@rutrum.edu']
-  },
-  {
-    id: '273',
-    displayName: 'Keegan Mayo',
-    name: {
-      familyName: ['Keegan'],
-      givenName: ['Mayo']
-    },
-    phones: ['1-351-848-2796'],
-    emails: ['ridiculus@Nuncsed.ca']
-  },
-  {
-    id: '276',
-    displayName: 'Wang Cote',
-    name: {
-      familyName: ['Wang'],
-      givenName: ['Cote']
-    },
-    phones: ['1-439-568-2013'],
-    emails: ['Morbi@tinciduntduiaugue.org']
-  },
-  {
-    id: '279',
-    displayName: 'Hyatt Rowe',
-    name: {
-      familyName: ['Hyatt'],
-      givenName: ['Rowe']
-    },
-    phones: ['1-596-765-3807'],
-    emails: ['eu.erat.semper@enimnonnisi.com']
-  },
-  {
-    id: '282',
-    displayName: 'Cade Wyatt',
-    name: {
-      familyName: ['Cade'],
-      givenName: ['Wyatt']
-    },
-    phones: ['1-988-289-5924'],
-    emails: ['erat.nonummy@sedpedeCum.com']
-  },
-  {
-    id: '285',
-    displayName: 'Stephen Vincent',
-    name: {
-      familyName: ['Stephen'],
-      givenName: ['Vincent']
-    },
-    phones: ['1-954-435-1259'],
-    emails: ['nec.euismod@ultricies.ca']
-  },
-  {
-    id: '288',
-    displayName: 'Tobias Cherry',
-    name: {
-      familyName: ['Tobias'],
-      givenName: ['Cherry']
-    },
-    phones: ['1-270-763-1111'],
-    emails: ['Nulla.aliquet@sit.com']
-  },
-  {
-    id: '291',
-    displayName: 'Keane Trevino',
-    name: {
-      familyName: ['Keane'],
-      givenName: ['Trevino']
-    },
-    phones: ['1-794-929-8599'],
-    emails: ['sem.semper.erat@Aliquamnecenim.edu']
-  },
-  {
-    id: '294',
-    displayName: 'Kennedy Cooley',
-    name: {
-      familyName: ['Kennedy'],
-      givenName: ['Cooley']
-    },
-    phones: ['1-725-946-1901'],
-    emails: ['urna.justo@Duismienim.edu']
-  },
-  {
-    id: '297',
-    displayName: 'Lucian Pope',
-    name: {
-      familyName: ['Lucian'],
-      givenName: ['Pope']
-    },
-    phones: ['1-186-946-8356'],
-    emails: ['justo.Proin@dis.com']
-  },
-  {
-    id: '300',
-    displayName: 'Hu Combs',
-    name: {
-      familyName: ['Hu'],
-      givenName: ['Combs']
-    },
-    phones: ['1-398-488-5222'],
-    emails: ['faucibus.lectus@nuncsedpede.com']
-  }
-];
-
-var ContactsManager = {
-  contacts: contacts,
-  find: function contactsManager(fields, successCallback, errorCallback) {
-    var contacts = this.contacts.slice();
-    successCallback(contacts);
-  },
-  create: function contactsCreate(successCallback, errorCallback, contact) {
-    this.contacts.push(contact);
-    successCallback();
-  },
-  delete: function contactsDelete(successCallback, errorCallback, id) {
-    var count = contacts.length;
-    for (var i = 0; i < count; i++) {
-      if (contacts[i].id != id)
-        continue;
-      var oldContact = contacts.slice(i, 1);
-      successCallback(oldContact);
-      return;
-    }
-    errorCallback();
-  }
-};
-
 let { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import('resource://gre/modules/Geometry.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 
 const ContentPanning = {
   init: function cp_init() {
     ['mousedown', 'mouseup', 'mousemove'].forEach(function(type) {
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1328289666000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1328822681000">
   <emItems>
       <emItem  blockID="i58" id="webmaster@buzzzzvideos.info">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i41" id="{99079a25-328f-4bd4-be04-00955acaa0a7}">
                         <versionRange  minVersion="0.1" maxVersion="4.3.1.00" severity="1">
                     </versionRange>
@@ -62,22 +62,30 @@
                           </targetApplication>
                     </versionRange>
                                 <versionRange  minVersion=" " maxVersion="6.9.8">
                       <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="3.7a1pre" maxVersion="*" />
                           </targetApplication>
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i63" id="youtube@youtuber.com">
+                        <versionRange  minVersion="0" maxVersion="*">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i18" id="msntoolbar@msn.com">
                         <versionRange  minVersion=" " maxVersion="6.*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i13" id="{E8E88AB0-7182-11DF-904E-6045E0D72085}">
                         </emItem>
+      <emItem  blockID="i64" id="royal@facebook.com">
+                        <versionRange  minVersion="0" maxVersion="*">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i4" id="{4B3803EA-5230-4DC3-A7FC-33638F3D3542}">
                         <versionRange  minVersion="1.2" maxVersion="1.2">
                       <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="3.0a1" maxVersion="*" />
                           </targetApplication>
                     </versionRange>
                   </emItem>
       <emItem  blockID="i50" id="firebug@software.joehewitt.com">
@@ -157,16 +165,20 @@
       <emItem  blockID="i48" id="admin@youtubespeedup.com">
                         </emItem>
       <emItem  blockID="i20" id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
                         <versionRange  minVersion="0.1" maxVersion="5.2.0.7164" severity="1">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i47" id="youtube@youtube2.com">
                         </emItem>
+      <emItem  blockID="i62" id="jid0-EcdqvFOgWLKHNJPuqAnawlykCGZ@jetpack">
+                        <versionRange  minVersion="0" maxVersion="*">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i17" id="{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}">
                         <versionRange  minVersion="2.2" maxVersion="2.2">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i23" id="firefox@bandoo.com">
                         <versionRange  minVersion="5.0" maxVersion="5.0" severity="1">
                       <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="3.7a1pre" maxVersion="*" />
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -35,18 +35,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 ***** */
 "use strict";
 
-/*global Components, NetUtil, Services, XPCOMUtils */
-/*global DebuggerServer, DebuggerClient, SourceEditor */
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
@@ -290,17 +288,17 @@ DebuggerUI.prototype = {
           onDataAvailable: function (aRequest, aContext, aStream, aOffset, aCount) {
             chunks.push(NetUtil.readInputStreamToString(aStream, aCount));
           },
           onStopRequest: function (aRequest, aContext, aStatusCode) {
             if (!Components.isSuccessCode(aStatusCode)) {
               return this.logError(url, aStatusCode);
             }
 
-            this._onSourceLoaded(url, chunks.join(""));
+            this._onSourceLoaded(url, chunks.join(""), channel.contentType);
           }.bind(this)
         };
 
         channel.loadFlags = channel.LOAD_FROM_CACHE;
         channel.asyncOpen(streamListener, null);
         break;
     }
   },
@@ -320,31 +318,32 @@ DebuggerUI.prototype = {
 
   /**
    * Called when source has been loaded.
    *
    * @param string aSourceUrl
    *        The URL of the source script.
    * @param string aSourceText
    *        The text of the source script.
+   * @param string aContentType
+   *        The content type of the source script.
    */
-  _onSourceLoaded: function DebuggerUI__onSourceLoaded(aSourceUrl, aSourceText) {
+  _onSourceLoaded: function DebuggerUI__onSourceLoaded(aSourceUrl,
+                                                       aSourceText,
+                                                       aContentType) {
     let dbg = this.getDebugger(this.aWindow.gBrowser.selectedTab);
-    if (aSourceUrl.slice(-3) == ".js") {
-      dbg.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
-    } else {
-      dbg.editor.setMode(SourceEditor.MODES.HTML);
-    }
+    dbg.debuggerWindow.SourceScripts.setEditorMode(aSourceUrl, aContentType);
     dbg.editor.setText(aSourceText);
     let doc = dbg.frame.contentDocument;
     let scripts = doc.getElementById("scripts");
     let elt = scripts.getElementsByAttribute("value", aSourceUrl)[0];
     let script = elt.getUserData("sourceScript");
     script.loaded = true;
     script.text = aSourceText;
+    script.contentType = aContentType;
     elt.setUserData("sourceScript", script, null);
   }
 };
 
 /**
  * Various debugger UI preferences (currently just the pane height).
  */
 let DebuggerUIPreferences = {
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -33,19 +33,17 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the LGPL or the GPL. 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 *****/
 "use strict";
 
-/*global Components, XPCOMUtils, Services, StackFrames, ThreadState, dump */
 const Cu = Components.utils;
-
 const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import('resource://gre/modules/Services.jsm');
 
 /**
  * Object mediating visual changes and event listeners between the debugger and
  * the html view.
@@ -1066,32 +1064,75 @@ DebuggerView.Scripts = {
    */
   empty: function DVS_empty() {
     while (this._scripts.firstChild) {
       this._scripts.removeChild(this._scripts.firstChild);
     }
   },
 
   /**
+   * Checks whether the script with the specified URL is among the scripts
+   * known to the debugger and shown in the list.
+   *
+   * @param string aUrl
+   *        The script URL.
+   */
+  contains: function DVS_contains(aUrl) {
+    if (this._scripts.getElementsByAttribute("value", aUrl).length > 0) {
+      return true;
+    }
+    return false;
+  },
+
+  /**
+   * Checks whether the script with the specified URL is selected in the list.
+   *
+   * @param string aUrl
+   *        The script URL.
+   */
+  isSelected: function DVS_isSelected(aUrl) {
+    if (this._scripts.selectedItem &&
+        this._scripts.selectedItem.value == aUrl) {
+      return true;
+    }
+    return false;
+  },
+
+  /**
+   * Selects the script with the specified URL from the list.
+   *
+   * @param string aUrl
+   *        The script URL.
+   */
+   selectScript: function DVS_selectScript(aUrl) {
+    for (let i = 0; i < this._scripts.itemCount; i++) {
+      if (this._scripts.getItemAtIndex(i).value == aUrl) {
+        this._scripts.selectedIndex = i;
+        break;
+      }
+    }
+   },
+
+  /**
    * Adds a script to the scripts container.
    * If the script already exists (was previously added), null is returned.
    * Otherwise, the newly created element is returned.
    *
    * @param string aUrl
    *        The script url.
    * @param string aScript
    *        The source script.
    * @param string aScriptNameText
    *        Optional, title displayed instead of url.
    * @return object
    *         The newly created html node representing the added script.
    */
   addScript: function DVS_addScript(aUrl, aSource, aScriptNameText) {
     // make sure we don't duplicate anything
-    if (this._scripts.getElementsByAttribute("value", aUrl).length > 0) {
+    if (this.contains(aUrl)) {
       return null;
     }
 
     let script = this._scripts.appendItem(aScriptNameText || aUrl, aUrl);
     script.setUserData("sourceScript", aSource, null);
     this._scripts.selectedItem = script;
     return script;
   },
--- a/browser/devtools/debugger/debugger.js
+++ b/browser/devtools/debugger/debugger.js
@@ -36,16 +36,18 @@
  * 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 ***** */
 
 "use strict";
 
+Components.utils.import("resource:///modules/source-editor.jsm");
+
 var gInitialized = false;
 var gClient = null;
 var gTabClient = null;
 
 
 function initDebugger()
 {
   window.removeEventListener("DOMContentLoaded", initDebugger, false);
@@ -250,21 +252,29 @@ var StackFrames = {
       DebuggerView.Stackframes.highlightFrame(this.selectedFrame, false);
     }
 
     this.selectedFrame = aDepth;
     if (aDepth !== null) {
       DebuggerView.Stackframes.highlightFrame(this.selectedFrame, true);
     }
 
-    // Display the local variables.
     let frame = this.activeThread.cachedFrames[aDepth];
     if (!frame) {
       return;
     }
+    // Move the editor's caret to the proper line.
+    if (DebuggerView.Scripts.isSelected(frame.where.url) && frame.where.line) {
+      window.editor.setCaretPosition(frame.where.line - 1);
+    } else if (DebuggerView.Scripts.contains(frame.where.url)) {
+      DebuggerView.Scripts.selectScript(frame.where.url);
+      SourceScripts.onChange({ target: DebuggerView.Scripts._scripts });
+      window.editor.setCaretPosition(frame.where.line - 1);
+    }
+    // Display the local variables.
     let localScope = DebuggerView.Properties.localScope;
     localScope.empty();
     // Add "this".
     if (frame["this"]) {
       let thisVar = localScope.addVar("this");
       thisVar.setGrip({ "type": frame["this"].type,
                         "class": frame["this"].class });
       this._addExpander(thisVar, frame["this"]);
@@ -430,56 +440,95 @@ var SourceScripts = {
    */
   disconnect: function TS_disconnect() {
     this.activeThread.removeListener("paused", this.onPaused);
     this.activeThread.removeListener("scriptsadded", this.onScripts);
     this.activeThread.removeListener("scriptscleared", this.onScriptsCleared);
   },
 
   /**
-   * Handler for the thread client's paused notification.
+   * Handler for the thread client's paused notification. This is triggered only
+   * once, to retrieve the list of scripts known to the server from before the
+   * client was ready to handle new script notifications.
    */
   onPaused: function SS_onPaused() {
+    this.activeThread.removeListener("paused", this.onPaused);
     this.activeThread.fillScripts();
   },
 
   /**
    * Handler for the debugger client's unsolicited newScript notification.
    */
   onNewScript: function SS_onNewScript(aNotification, aPacket) {
     this._addScript({ url: aPacket.url, startLine: aPacket.startLine });
   },
 
   /**
    * Handler for the thread client's scriptsadded notification.
    */
   onScripts: function SS_onScripts() {
-    this.onScriptsCleared();
     for each (let script in this.activeThread.cachedScripts) {
       this._addScript(script);
     }
   },
 
   /**
    * Handler for the thread client's scriptscleared notification.
    */
   onScriptsCleared: function SS_onScriptsCleared() {
     DebuggerView.Scripts.empty();
   },
 
   /**
    * Handler for changes on the selected source script.
    */
-  onChange: function SS_onClick(aEvent) {
+  onChange: function SS_onChange(aEvent) {
     let scripts = aEvent.target;
+    if (!scripts.selectedItem) {
+      return;
+    }
     let script = scripts.selectedItem.getUserData("sourceScript");
+    this.setEditorMode(script.url, script.contentType);
     this._showScript(script);
   },
 
   /**
+   * Sets the proper editor mode (JS or HTML) according to the specified
+   * content type, or by determining the type from the URL.
+   *
+   * @param string aUrl
+   *        The script URL.
+   * @param string aContentType [optional]
+   *        The script content type.
+   */
+  setEditorMode: function SS_setEditorMode(aUrl, aContentType) {
+    if (aContentType) {
+      if (/javascript/.test(aContentType)) {
+        window.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
+      } else {
+        window.editor.setMode(SourceEditor.MODES.HTML);
+      }
+      return;
+    }
+
+    let url = aUrl;
+    // Trim the query part.
+    let q = url.indexOf('?');
+    if (q > -1) {
+      url = url.slice(0, q);
+    }
+
+    if (url.slice(-3) == ".js") {
+      window.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
+    } else {
+      window.editor.setMode(SourceEditor.MODES.HTML);
+    }
+  },
+
+  /**
    * Add the specified script to the list and display it in the editor if the
    * editor is empty.
    */
   _addScript: function SS_addScript(aScript) {
     DebuggerView.Scripts.addScript(aScript.url, aScript);
 
     if (window.editor.getCharCount() == 0) {
       this._showScript(aScript);
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -65,27 +65,31 @@ include $(topsrcdir)/config/rules.mk
 	browser_dbg_panesize.js \
 	browser_dbg_stack-01.js \
 	browser_dbg_stack-02.js \
 	browser_dbg_stack-03.js \
 	browser_dbg_stack-04.js \
 	browser_dbg_location-changes.js \
 	browser_dbg_script-switching.js \
 	browser_dbg_pause-resume.js \
+	browser_dbg_update-editor-mode.js \
+	browser_dbg_select-line.js \
 	head.js \
 	$(NULL)
 
 _BROWSER_TEST_PAGES = \
 	browser_dbg_tab1.html \
 	browser_dbg_tab2.html \
 	browser_dbg_debuggerstatement.html \
 	browser_dbg_stack.html \
 	browser_dbg_script-switching.html \
 	test-script-switching-01.js \
 	test-script-switching-02.js \
 	browser_dbg_frame-parameters.html \
+	browser_dbg_update-editor-mode.html \
+	test-editor-mode \
 	$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 
 libs:: $(_BROWSER_TEST_PAGES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/devtools/debugger/test/browser_dbg_script-switching.js
+++ b/browser/devtools/debugger/test/browser_dbg_script-switching.js
@@ -28,45 +28,34 @@ function test()
 
     testScriptsDisplay();
   });
 }
 
 function testScriptsDisplay() {
   gPane.activeThread.addOneTimeListener("scriptsadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
-      let count = 0;
-      gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
-                                        function onScriptLoad() {
-        // Skip the first change event, since we're only interested in the
-        // second.
-        if (count++ < 1) {
-          return;
-        }
-        gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
-                                             onScriptLoad);
-        gScripts = gDebugger.DebuggerView.Scripts._scripts;
+      gScripts = gDebugger.DebuggerView.Scripts._scripts;
 
-        is(gDebugger.StackFrames.activeThread.state, "paused",
-          "Should only be getting stack frames while paused.");
+      is(gDebugger.StackFrames.activeThread.state, "paused",
+        "Should only be getting stack frames while paused.");
+
+      is(gScripts.itemCount, 2, "Found the expected number of scripts.");
 
-        is(gScripts.itemCount, 2, "Found the expected number of scripts.");
-
-        ok(gDebugger.editor.getText().search(/debugger/) != -1,
-          "The correct script was loaded initially.");
+      ok(gDebugger.editor.getText().search(/debugger/) != -1,
+        "The correct script was loaded initially.");
 
-        gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
-                                          function onChange() {
-          gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
-                                               onChange);
-          testSwitchPaused();
-        });
-        gScripts.selectedIndex = 0;
-        gDebugger.SourceScripts.onChange({ target: gScripts });
+      gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                        function onChange() {
+        gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                             onChange);
+        testSwitchPaused();
       });
+      gScripts.selectedIndex = 0;
+      gDebugger.SourceScripts.onChange({ target: gScripts });
     }}, 0);
   });
 
   gDebuggee.firstCall();
 }
 
 function testSwitchPaused()
 {
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_select-line.js
@@ -0,0 +1,81 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Make sure that selecting a stack frame loads the right script in the editor
+ * pane and highlights the proper line.
+ */
+
+const TAB_URL = "http://example.com/browser/browser/devtools/debugger/" +
+                "test/browser_dbg_script-switching.html";
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
+
+var gPane = null;
+var gTab = null;
+var gDebuggee = null;
+var gDebugger = null;
+var gScripts = null;
+
+function test()
+{
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPane = aPane;
+    gDebugger = gPane.debuggerWindow;
+
+    testSelectLine();
+  });
+}
+
+function testSelectLine() {
+  gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+    Services.tm.currentThread.dispatch({ run: function() {
+      gScripts = gDebugger.DebuggerView.Scripts._scripts;
+
+      is(gDebugger.StackFrames.activeThread.state, "paused",
+        "Should only be getting stack frames while paused.");
+
+      is(gScripts.itemCount, 2, "Found the expected number of scripts.");
+
+      ok(gDebugger.editor.getText().search(/debugger/) != -1,
+        "The correct script was loaded initially.");
+
+      // getCaretPosition is 0-based.
+      is(gDebugger.editor.getCaretPosition().line, 5,
+         "The correct line is selected.");
+
+      gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                        function onChange() {
+        gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                             onChange);
+        ok(gDebugger.editor.getText().search(/debugger/) == -1,
+          "The second script is no longer displayed.");
+
+        ok(gDebugger.editor.getText().search(/firstCall/) != -1,
+          "The first script is displayed.");
+
+        // Yield control back to the event loop so that the debugger has a
+        // chance to highlight the proper line.
+        executeSoon(function(){
+          // getCaretPosition is 0-based.
+          is(gDebugger.editor.getCaretPosition().line, 4,
+             "The correct line is selected.");
+
+          gDebugger.StackFrames.activeThread.resume(function() {
+            removeTab(gTab);
+            finish();
+          });
+        });
+      });
+
+      // Click the oldest stack frame.
+      let element = gDebugger.document.getElementById("stackframe-3");
+      EventUtils.synthesizeMouseAtCenter(element, {}, gDebugger);
+    }}, 0);
+  });
+
+  gDebuggee.firstCall();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+	<head>
+		<title>Browser Debugger Update Editor Mode Test</title>
+    <!-- Any copyright is dedicated to the Public Domain.
+         http://creativecommons.org/publicdomain/zero/1.0/ -->
+    <script type="text/javascript" src="test-script-switching-01.js?q=a"></script>
+    <script type="text/javascript" src="test-editor-mode?a=b"></script>
+	</head>
+	<body>
+	</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Make sure that updating the editor mode sets the right highlighting engine,
+ * and script URIs with extra query parameters also get the right engine.
+ */
+
+const TAB_URL = "http://example.com/browser/browser/devtools/debugger/" +
+                "test/browser_dbg_update-editor-mode.html";
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
+
+var gPane = null;
+var gTab = null;
+var gDebuggee = null;
+var gDebugger = null;
+var gScripts = null;
+
+function test()
+{
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPane = aPane;
+    gDebugger = gPane.debuggerWindow;
+
+    testScriptsDisplay();
+  });
+}
+
+function testScriptsDisplay() {
+  gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+    Services.tm.currentThread.dispatch({ run: function() {
+      gScripts = gDebugger.DebuggerView.Scripts._scripts;
+
+      is(gDebugger.StackFrames.activeThread.state, "paused",
+        "Should only be getting stack frames while paused.");
+
+      is(gScripts.itemCount, 2, "Found the expected number of scripts.");
+
+      is(gDebugger.editor.getMode(), SourceEditor.MODES.HTML,
+         "Found the expected editor mode.");
+
+      ok(gDebugger.editor.getText().search(/debugger/) != -1,
+        "The correct script was loaded initially.");
+
+      gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                        function onChange() {
+        gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                             onChange);
+        testSwitchPaused();
+      });
+      gScripts.selectedIndex = 0;
+      gDebugger.SourceScripts.onChange({ target: gScripts });
+    }}, 0);
+  });
+
+  gDebuggee.firstCall();
+}
+
+function testSwitchPaused()
+{
+  ok(gDebugger.editor.getText().search(/debugger/) == -1,
+    "The second script is no longer displayed.");
+
+  ok(gDebugger.editor.getText().search(/firstCall/) != -1,
+    "The first script is displayed.");
+
+  is(gDebugger.editor.getMode(), SourceEditor.MODES.JAVASCRIPT,
+     "Found the expected editor mode.");
+
+  gDebugger.StackFrames.activeThread.resume(function() {
+    removeTab(gTab);
+    finish();
+  });
+}
copy from browser/devtools/debugger/test/test-script-switching-02.js
copy to browser/devtools/debugger/test/test-editor-mode
--- a/browser/devtools/debugger/test/test-script-switching-02.js
+++ b/browser/devtools/debugger/test/test-script-switching-02.js
@@ -1,6 +1,7 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function secondCall() {
+  // This comment is useful for browser_dbg_select-line.js.
   eval("debugger;");
 }
--- a/browser/devtools/tilt/Tilt.jsm
+++ b/browser/devtools/tilt/Tilt.jsm
@@ -241,17 +241,17 @@ Tilt.prototype = {
    * A node was selected in the Inspector.
    * Called from InspectorUI.
    *
    * @param {Element} aNode
    *                  the newly selected node
    */
   update: function T_update(aNode) {
     if (this.currentInstance) {
-      this.currentInstance.presenter.highlightNode(aNode);
+      this.currentInstance.presenter.highlightNode(aNode, "moveIntoView");
     }
   },
 
   /**
    * Add the browser event listeners to handle state changes.
    * Called from InspectorUI.
    */
   setup: function T_setup()
--- a/browser/devtools/tilt/TiltUtils.jsm
+++ b/browser/devtools/tilt/TiltUtils.jsm
@@ -543,28 +543,16 @@ TiltUtils.getWindowId = function TU_getW
   }
 
   return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                 .getInterface(Ci.nsIDOMWindowUtils)
                 .currentInnerWindowID;
 };
 
 /**
- * Gets the markup document viewer zoom for the currently selected browser.
- *
- * @param {Window} aChromeWindow
- *                 the top-level browser window
- *
- * @return {Number} the zoom ammount
- */
-TiltUtils.getDocumentZoom = function TU_getDocumentZoom(aChromeWindow) {
-  return aChromeWindow.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom;
-};
-
-/**
  * Sets the markup document viewer zoom for the currently selected browser.
  *
  * @param {Window} aChromeWindow
  *                 the top-level browser window
  *
  * @param {Number} the zoom ammount
  */
 TiltUtils.setDocumentZoom = function TU_setDocumentZoom(aChromeWindow, aZoom) {
--- a/browser/devtools/tilt/TiltVisualizer.jsm
+++ b/browser/devtools/tilt/TiltVisualizer.jsm
@@ -55,16 +55,17 @@ const INVISIBLE_ELEMENTS = {
   "title": true
 };
 
 const STACK_THICKNESS = 15;
 const WIREFRAME_COLOR = [0, 0, 0, 0.25];
 const INTRO_TRANSITION_DURATION = 50;
 const OUTRO_TRANSITION_DURATION = 40;
 const INITIAL_Z_TRANSLATION = 400;
+const MOVE_INTO_VIEW_ACCURACY = 50;
 
 const MOUSE_CLICK_THRESHOLD = 10;
 const MOUSE_INTRO_DELAY = 10;
 const ARCBALL_SENSITIVITY = 0.5;
 const ARCBALL_ROTATION_STEP = 0.15;
 const ARCBALL_TRANSLATION_STEP = 35;
 const ARCBALL_ZOOM_STEP = 0.1;
 const ARCBALL_ZOOM_MIN = -3000;
@@ -245,17 +246,17 @@ TiltVisualizer.Presenter = function TV_P
     v3: vec3.create()
   };
 
   /**
    * Scene transformations, exposing offset, translation and rotation.
    * Modified by events in the controller through delegate functions.
    */
   this.transforms = {
-    zoom: TiltUtils.getDocumentZoom(aChromeWindow),
+    zoom: 1,
     offset: vec3.create(),      // mesh offset, aligned to the viewport center
     translation: vec3.create(), // scene translation, on the [x, y, z] axis
     rotation: quat4.create()    // scene rotation, expressed as a quaternion
   };
 
   /**
    * Variables holding information about the initial and current node selected.
    */
@@ -275,30 +276,36 @@ TiltVisualizer.Presenter = function TV_P
   this.frames = 0;
 
   /**
    * The initialization logic.
    */
   let setup = function TVP_setup()
   {
     let renderer = this.renderer;
+    let inspector = this.chromeWindow.InspectorUI;
 
     // if the renderer was destroyed, don't continue setup
     if (!renderer || !renderer.context) {
       return;
     }
 
     // create the visualization shaders and program to draw the stacks mesh
     this.visualizationProgram = new renderer.Program({
       vs: TiltVisualizer.MeshShader.vs,
       fs: TiltVisualizer.MeshShader.fs,
       attributes: ["vertexPosition", "vertexTexCoord", "vertexColor"],
       uniforms: ["mvMatrix", "projMatrix", "sampler"]
     });
 
+    // get the document zoom to properly scale the visualization
+    if (inspector.highlighter) {
+      this.transforms.zoom = inspector.highlighter.zoom;
+    }
+
     this.setupTexture();
     this.setupMeshData();
     this.setupEventListeners();
     this.canvas.focus();
   }.bind(this);
 
   /**
    * The animation logic.
@@ -541,19 +548,18 @@ TiltVisualizer.Presenter.prototype = {
     if (!this._initialSelection) {
       this._initialSelection = true;
       this.highlightNode(this.chromeWindow.InspectorUI.selection);
     }
 
     if (!this._initialMeshConfiguration) {
       this._initialMeshConfiguration = true;
 
-      let zoom = this.transforms.zoom;
-      let width = Math.min(aData.meshWidth * zoom, renderer.width);
-      let height = Math.min(aData.meshHeight * zoom, renderer.height);
+      let width = renderer.width;
+      let height = renderer.height;
 
       // set the necessary mesh offsets
       this.transforms.offset[0] = -width * 0.5;
       this.transforms.offset[1] = -height * 0.5;
 
       // make sure the canvas is opaque now that the initialization is finished
       this.canvas.style.background = TiltVisualizerStyle.canvas.background;
 
@@ -615,36 +621,38 @@ TiltVisualizer.Presenter.prototype = {
     this.contentWindow.addEventListener("resize", this.onResize, false);
   },
 
   /**
    * Called when the content window of the current browser is resized.
    */
   onResize: function TVP_onResize(e)
   {
-    let zoom = TiltUtils.getDocumentZoom(this.chromeWindow);
+    let zoom = this.chromeWindow.InspectorUI.highlighter.zoom;
     let width = e.target.innerWidth * zoom;
     let height = e.target.innerHeight * zoom;
 
     // handle aspect ratio changes to update the projection matrix
     this.renderer.width = width;
     this.renderer.height = height;
 
     this.redraw = true;
   },
 
   /**
    * Highlights a specific node.
    *
    * @param {Element} aNode
    *                  the html node to be highlighted
+   * @param {String} aFlags
+   *                 flags specifying highlighting options
    */
-  highlightNode: function TVP_highlightNode(aNode)
+  highlightNode: function TVP_highlightNode(aNode, aFlags)
   {
-    this.highlightNodeFor(this.traverseData.nodes.indexOf(aNode));
+    this.highlightNodeFor(this.traverseData.nodes.indexOf(aNode), aFlags);
   },
 
   /**
    * Picks a stacked dom node at the x and y screen coordinates and highlights
    * the selected node in the mesh.
    *
    * @param {Number} x
    *                 the current horizontal coordinate of the mouse
@@ -693,18 +701,20 @@ TiltVisualizer.Presenter.prototype = {
   },
 
   /**
    * Sets the corresponding highlight coordinates and color based on the
    * information supplied.
    *
    * @param {Number} aNodeIndex
    *                 the index of the node in the this.traverseData array
+   * @param {String} aFlags
+   *                 flags specifying highlighting options
    */
-  highlightNodeFor: function TVP_highlightNodeFor(aNodeIndex)
+  highlightNodeFor: function TVP_highlightNodeFor(aNodeIndex, aFlags)
   {
     this.redraw = true;
 
     // if the node was already selected, don't do anything
     if (this._currentSelection === aNodeIndex) {
       return;
     }
 
@@ -739,16 +749,27 @@ TiltVisualizer.Presenter.prototype = {
     vec3.set([x,     y + h, z * STACK_THICKNESS], highlight.v3);
 
     this._currentSelection = aNodeIndex;
 
     this.chromeWindow.InspectorUI.inspectNode(node,
       this.contentWindow.innerHeight < y ||
       this.contentWindow.pageYOffset > 0);
 
+    // if something is highlighted, make sure it's inside the current viewport;
+    // the point which should be moved into view is considered the center [x, y]
+    // position along the top edge of the currently selected node
+
+    if (aFlags && aFlags.indexOf("moveIntoView") !== -1)
+    {
+      this.controller.arcball.moveIntoView(vec3.lerp(
+        vec3.scale(this.highlight.v0, this.transforms.zoom, []),
+        vec3.scale(this.highlight.v1, this.transforms.zoom, []), 0.5));
+    }
+
     Services.obs.notifyObservers(null, this.NOTIFICATIONS.HIGHLIGHTING, null);
   },
 
   /**
    * Deletes a node from the visualization mesh.
    *
    * @param {Number} aNodeIndex
    *                 the index of the node in the this.traverseData array;
@@ -808,17 +829,17 @@ TiltVisualizer.Presenter.prototype = {
         }
       } else {
         if ("function" === typeof aProperties.onfail) {
           aProperties.onfail();
         }
       }
     }, false);
 
-    let zoom = TiltUtils.getDocumentZoom(this.chromeWindow);
+    let zoom = this.chromeWindow.InspectorUI.highlighter.zoom;
     let width = this.renderer.width * zoom;
     let height = this.renderer.height * zoom;
     let mesh = this.meshStacks;
     x *= zoom;
     y *= zoom;
 
     // create a ray following the mouse direction from the near clipping plane
     // to the far clipping plane, to check for intersections with the mesh,
@@ -992,16 +1013,17 @@ TiltVisualizer.Controller = function TV_
    * A canvas overlay on which mouse and keyboard event listeners are attached.
    */
   this.canvas = aCanvas;
 
   /**
    * Save a reference to the presenter to modify its model-view transforms.
    */
   this.presenter = aPresenter;
+  this.presenter.controller = this;
 
   /**
    * The initial controller dimensions and offset, in pixels.
    */
   this.zoom = aPresenter.transforms.zoom;
   this.left = (aPresenter.contentWindow.pageXOffset || 0) * this.zoom;
   this.top = (aPresenter.contentWindow.pageYOffset || 0) * this.zoom;
   this.width = aCanvas.width;
@@ -1251,17 +1273,17 @@ TiltVisualizer.Controller.prototype = {
     this.arcball.cancelKeyEvents();
   },
 
   /**
    * Called when the content window of the current browser is resized.
    */
   onResize: function TVC_onResize(e)
   {
-    let zoom = TiltUtils.getDocumentZoom(this.presenter.chromeWindow);
+    let zoom = this.presenter.chromeWindow.InspectorUI.highlighter.zoom;
     let width = e.target.innerWidth * zoom;
     let height = e.target.innerHeight * zoom;
 
     this.arcball.resize(width, height);
   },
 
   /**
    * Checks if this object was initialized properly.
@@ -1724,16 +1746,35 @@ TiltVisualizer.Arcball.prototype = {
    */
   cancelMouseEvents: function TVA_cancelMouseEvents()
   {
     this._rotating = false;
     this._mouseButton = -1;
   },
 
   /**
+   * Moves a target point into view only if it's outside the currently visible
+   * area bounds (in which case it also resets any additional transforms).
+   *
+   * @param {Arary} aPoint
+   *                the [x, y] point which should be brought into view
+   */
+  moveIntoView: function TVA_moveIntoView(aPoint) {
+    let visiblePointX = -(this._currentTrans[0] + this._additionalTrans[0]);
+    let visiblePointY = -(this._currentTrans[1] + this._additionalTrans[1]);
+
+    if (aPoint[1] - visiblePointY - MOVE_INTO_VIEW_ACCURACY > this.height ||
+        aPoint[1] - visiblePointY + MOVE_INTO_VIEW_ACCURACY < 0 ||
+        aPoint[0] - visiblePointX > this.width ||
+        aPoint[0] - visiblePointX < 0) {
+      this.reset([0, -aPoint[1]]);
+    }
+  },
+
+  /**
    * Resize this implementation to use different bounds.
    * This function is automatically called when the arcball is created.
    *
    * @param {Number} newWidth
    *                 the new width of canvas
    * @param {Number} newHeight
    *                 the new  height of canvas
    * @param {Number} newRadius
--- a/browser/devtools/tilt/test/Makefile.in
+++ b/browser/devtools/tilt/test/Makefile.in
@@ -71,16 +71,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_tilt_math02.js \
 	browser_tilt_math03.js \
 	browser_tilt_math04.js \
 	browser_tilt_math05.js \
 	browser_tilt_math06.js \
 	browser_tilt_math07.js \
 	browser_tilt_picking.js \
 	browser_tilt_picking_delete.js \
+	browser_tilt_picking_highlight01-offs.js \
 	browser_tilt_picking_highlight01.js \
 	browser_tilt_picking_highlight02.js \
 	browser_tilt_picking_highlight03.js \
 	browser_tilt_utils01.js \
 	browser_tilt_utils02.js \
 	browser_tilt_utils03.js \
 	browser_tilt_utils04.js \
 	browser_tilt_utils05.js \
--- a/browser/devtools/tilt/test/browser_tilt_picking.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking.js
@@ -18,17 +18,17 @@ function test() {
     createTilt({
       onTiltOpen: function(instance)
       {
         let presenter = instance.presenter;
         let canvas = presenter.canvas;
 
         presenter.onSetupMesh = function() {
 
-          presenter.pickNode(canvas.width / 2, canvas.height / 2, {
+          presenter.pickNode(canvas.width / 2, 10, {
             onpick: function(data)
             {
               ok(data.index > 0,
                 "Simply picking a node didn't work properly.");
               ok(!presenter.highlight.disabled,
                 "After only picking a node, it shouldn't be highlighted.");
 
               Services.obs.addObserver(cleanup, DESTROYED, false);
--- a/browser/devtools/tilt/test/browser_tilt_picking_delete.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking_delete.js
@@ -19,18 +19,17 @@ function test() {
   createTab(function() {
     createTilt({
       onTiltOpen: function(instance)
       {
         presenter = instance.presenter;
         Services.obs.addObserver(whenNodeRemoved, NODE_REMOVED, false);
 
         presenter.onSetupMesh = function() {
-          presenter.highlightNodeAt(presenter.canvas.width / 2,
-                                    presenter.canvas.height / 2, {
+          presenter.highlightNodeAt(presenter.canvas.width / 2, 10, {
             onpick: function()
             {
               ok(presenter._currentSelection > 0,
                 "Highlighting a node didn't work properly.");
               ok(!presenter.highlight.disabled,
                 "After highlighting a node, it should be highlighted. D'oh.");
 
               presenter.deleteNode();
new file mode 100644
--- /dev/null
+++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight01-offs.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+let presenter;
+
+function test() {
+  if (!isTiltEnabled()) {
+    info("Skipping highlight test because Tilt isn't enabled.");
+    return;
+  }
+  if (!isWebGLSupported()) {
+    info("Skipping highlight test because WebGL isn't supported.");
+    return;
+  }
+
+  requestLongerTimeout(10);
+  waitForExplicitFinish();
+
+  createTab(function() {
+    createTilt({
+      onTiltOpen: function(instance)
+      {
+        presenter = instance.presenter;
+        Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
+
+        presenter.onInitializationFinished = function() {
+          let contentDocument = presenter.contentWindow.document;
+          let div = contentDocument.getElementById("far-far-away");
+
+          presenter.highlightNode(div, "moveIntoView");
+        };
+      }
+    });
+  });
+}
+
+function whenHighlighting() {
+  ok(presenter._currentSelection > 0,
+    "Highlighting a node didn't work properly.");
+  ok(!presenter.highlight.disabled,
+    "After highlighting a node, it should be highlighted. D'oh.");
+  ok(presenter.controller.arcball._resetInterval,
+    "Highlighting a node that's not already visible should trigger a reset!");
+
+  executeSoon(function() {
+    Services.obs.addObserver(whenUnhighlighting, UNHIGHLIGHTING, false);
+    presenter.highlightNode(null);
+  });
+}
+
+function whenUnhighlighting() {
+  ok(presenter._currentSelection < 0,
+    "Unhighlighting a should remove the current selection.");
+  ok(presenter.highlight.disabled,
+    "After unhighlighting a node, it shouldn't be highlighted anymore. D'oh.");
+
+  executeSoon(function() {
+    Services.obs.addObserver(cleanup, DESTROYED, false);
+    InspectorUI.closeInspectorUI();
+  });
+}
+
+function cleanup() {
+  Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING);
+  Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING);
+  Services.obs.removeObserver(cleanup, DESTROYED);
+  gBrowser.removeCurrentTab();
+  finish();
+}
--- a/browser/devtools/tilt/test/browser_tilt_picking_highlight01.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight01.js
@@ -22,28 +22,30 @@ function test() {
       {
         presenter = instance.presenter;
         Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
 
         presenter.onSetupMesh = function() {
           let contentDocument = presenter.contentWindow.document;
           let div = contentDocument.getElementById("first-law");
 
-          presenter.highlightNode(div);
+          presenter.highlightNode(div, "moveIntoView");
         };
       }
     });
   });
 }
 
 function whenHighlighting() {
   ok(presenter._currentSelection > 0,
     "Highlighting a node didn't work properly.");
   ok(!presenter.highlight.disabled,
     "After highlighting a node, it should be highlighted. D'oh.");
+  ok(!presenter.controller.arcball._resetInterval,
+    "Highlighting a node that's already visible shouldn't trigger a reset.");
 
   executeSoon(function() {
     Services.obs.addObserver(whenUnhighlighting, UNHIGHLIGHTING, false);
     presenter.highlightNode(null);
   });
 }
 
 function whenUnhighlighting() {
--- a/browser/devtools/tilt/test/browser_tilt_picking_highlight02.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight02.js
@@ -19,18 +19,17 @@ function test() {
   createTab(function() {
     createTilt({
       onTiltOpen: function(instance)
       {
         presenter = instance.presenter;
         Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
 
         presenter.onSetupMesh = function() {
-          presenter.highlightNodeAt(presenter.canvas.width / 2,
-                                    presenter.canvas.height / 2);
+          presenter.highlightNodeAt(presenter.canvas.width / 2, 10);
         };
       }
     });
   });
 }
 
 function whenHighlighting() {
   ok(presenter._currentSelection > 0,
--- a/browser/devtools/tilt/test/browser_tilt_zoom.js
+++ b/browser/devtools/tilt/test/browser_tilt_zoom.js
@@ -1,23 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const ZOOM = 2;
 const RESIZE = 50;
 
 function test() {
-  let random = Math.random() * 10;
-
-  TiltUtils.setDocumentZoom(window, random);
-  ok(isApprox(TiltUtils.getDocumentZoom(window), random),
-    "The getDocumentZoom utility function didn't return the expected results.");
-
-
   if (!isTiltEnabled()) {
     info("Skipping controller test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
     info("Skipping controller test because WebGL isn't supported.");
     return;
   }
@@ -27,16 +20,19 @@ function test() {
   createTab(function() {
     createTilt({
       onInspectorOpen: function()
       {
         TiltUtils.setDocumentZoom(window, ZOOM);
       },
       onTiltOpen: function(instance)
       {
+        ok(isApprox(InspectorUI.highlighter.zoom, ZOOM),
+          "The Highlighter zoom doesn't have the expected results.");
+
         ok(isApprox(instance.presenter.transforms.zoom, ZOOM),
           "The presenter transforms zoom wasn't initially set correctly.");
 
         let contentWindow = gBrowser.selectedBrowser.contentWindow;
         let initialWidth = contentWindow.innerWidth;
         let initialHeight = contentWindow.innerHeight;
 
         let renderer = instance.presenter.renderer;
@@ -69,20 +65,21 @@ function test() {
           ok(isApprox(contentWindow.innerWidth * ZOOM, arcball.width, 1),
             "The arcball width wasn't set correctly after the resize.");
           ok(isApprox(contentWindow.innerHeight * ZOOM, arcball.height, 1),
             "The arcball height wasn't set correctly after the resize.");
 
 
           window.resizeBy(RESIZE * ZOOM, RESIZE * ZOOM);
 
+
           Services.obs.addObserver(cleanup, DESTROYED, false);
           InspectorUI.closeInspectorUI();
         });
-      },
+      }
     });
   });
 }
 
 function cleanup() {
   Services.obs.removeObserver(cleanup, DESTROYED);
   gBrowser.removeCurrentTab();
   finish();
--- a/browser/devtools/tilt/test/head.js
+++ b/browser/devtools/tilt/test/head.js
@@ -32,16 +32,19 @@ const DEFAULT_HTML = "data:text/html," +
       "<div>" +
         "A robot must obey the orders given to it by human beings, except " +
         "where such orders would conflict with the First Law." +
       "</div>" +
       "<div>" +
         "A robot must protect its own existence as long as such protection " +
         "does not conflict with the First or Second Laws." +
       "</div>" +
+      "<div id='far-far-away' style='position: absolute; top: 250%;'>" +
+        "I like bacon." +
+      "</div>" +
     "<body>" +
   "</html>";
 
 const INSPECTOR_OPENED = InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED;
 const INSPECTOR_CLOSED = InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED;
 
 const INITIALIZING = Tilt.NOTIFICATIONS.INITIALIZING;
 const INITIALIZED = Tilt.NOTIFICATIONS.INITIALIZED;
@@ -132,45 +135,51 @@ function createTilt(callbacks, close) {
 
   function onInspectorOpen() {
     Services.obs.removeObserver(onInspectorOpen, INSPECTOR_OPENED);
 
     executeSoon(function() {
       if ("function" === typeof callbacks.onInspectorOpen) {
         callbacks.onInspectorOpen();
       }
-      Services.obs.addObserver(onTiltOpen, INITIALIZING, false);
-      Tilt.initialize();
+      executeSoon(function() {
+        Services.obs.addObserver(onTiltOpen, INITIALIZING, false);
+        Tilt.initialize();
+      });
     });
   }
 
   function onTiltOpen() {
     Services.obs.removeObserver(onTiltOpen, INITIALIZING);
 
     executeSoon(function() {
       if ("function" === typeof callbacks.onTiltOpen) {
         callbacks.onTiltOpen(Tilt.visualizers[Tilt.currentWindowId]);
       }
       if (close) {
-        Services.obs.addObserver(onTiltClose, DESTROYED, false);
-        Tilt.destroy(Tilt.currentWindowId);
+        executeSoon(function() {
+          Services.obs.addObserver(onTiltClose, DESTROYED, false);
+          Tilt.destroy(Tilt.currentWindowId);
+        });
       }
     });
   }
 
   function onTiltClose() {
     Services.obs.removeObserver(onTiltClose, DESTROYED);
 
     executeSoon(function() {
       if ("function" === typeof callbacks.onTiltClose) {
         callbacks.onTiltClose();
       }
       if (close) {
-        Services.obs.addObserver(onInspectorClose, INSPECTOR_CLOSED, false);
-        InspectorUI.closeInspectorUI();
+        executeSoon(function() {
+          Services.obs.addObserver(onInspectorClose, INSPECTOR_CLOSED, false);
+          InspectorUI.closeInspectorUI();
+        });
       }
     });
   }
 
   function onInspectorClose() {
     Services.obs.removeObserver(onInspectorClose, INSPECTOR_CLOSED);
 
     executeSoon(function() {
--- a/configure.in
+++ b/configure.in
@@ -5952,17 +5952,18 @@ else
   dnl No path specified, so look for javac and jar in $JAVA_HOME & $PATH.
   JAVA_PATH="$JAVA_HOME/bin:$PATH"
 fi
 
 MOZ_PATH_PROG(JAVA, java, :, [$JAVA_PATH])
 MOZ_PATH_PROG(JAVAC, javac, :, [$JAVA_PATH])
 MOZ_PATH_PROG(JAR, jar, :, [$JAVA_PATH])
 
-if test -n "${JAVA_BIN_PATH}" -o "$OS_TARGET" = Android; then
+if test -n "${JAVA_BIN_PATH}" -o \
+  \( "$OS_TARGET" = Android -a x"$MOZ_WIDGET_TOOLKIT" != x"gonk" \); then
   if test -z "$JAVA" -o "$JAVA" = ":" -o -z "$JAVAC" -o "$JAVAC" = ":" -o -z "$JAR" -o "$JAR" = ":"; then
     AC_MSG_ERROR([The programs java, javac and jar were not found.  Set \$JAVA_HOME to your java sdk directory or use --with-java-bin-path={java-bin-dir}])
   fi
 fi
 
 dnl ========================================================
 dnl = ANGLE OpenGL->D3D translator for WebGL
 dnl = * only applies to win32
@@ -8205,17 +8206,17 @@ AC_SUBST(MOZ_TREE_CAIRO)
 AC_SUBST(MOZ_CAIRO_CFLAGS)
 AC_SUBST(MOZ_CAIRO_LIBS)
 AC_SUBST(MOZ_TREE_PIXMAN)
 
 dnl ========================================================
 dnl qcms
 dnl ========================================================
 
-QCMS_LIBS='$(call EXPAND_LIBNAME_PATH,mozqcms,$(DEPTH)/gfx/qcms)'
+QCMS_LIBS='$(DEPTH)/gfx/qcms/$(LIB_PREFIX)mozqcms.$(LIB_SUFFIX)'
 AC_SUBST(QCMS_LIBS)
 
 dnl ========================================================
 dnl HarfBuzz
 dnl ========================================================
 MOZ_HARFBUZZ_LIBS='$(DEPTH)/gfx/harfbuzz/src/$(LIB_PREFIX)mozharfbuzz.$(LIB_SUFFIX)'
 AC_SUBST(MOZ_HARFBUZZ_LIBS)
 
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -4791,24 +4791,41 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     char name[512];
     PRUint32 nsid = tmp->GetNameSpaceID();
     nsAtomCString localName(tmp->NodeInfo()->NameAtom());
     nsCAutoString uri;
     if (tmp->OwnerDoc()->GetDocumentURI()) {
       tmp->OwnerDoc()->GetDocumentURI()->GetSpec(uri);
     }
 
-    if (nsid < ArrayLength(kNSURIs)) {
-      PR_snprintf(name, sizeof(name), "nsGenericElement%s %s %s", kNSURIs[nsid],
-                  localName.get(), uri.get());
+    nsAutoString id;
+    nsIAtom* idAtom = tmp->GetID();
+    if (idAtom) {
+      id.AppendLiteral(" id='");
+      id.Append(nsDependentAtomString(idAtom));
+      id.AppendLiteral("'");
     }
-    else {
-      PR_snprintf(name, sizeof(name), "nsGenericElement %s %s",
-                  localName.get(), uri.get());
+
+    nsAutoString classes;
+    const nsAttrValue* classAttrValue = tmp->GetClasses();
+    if (classAttrValue) {
+      classes.AppendLiteral(" class='");
+      nsAutoString classString;
+      classAttrValue->ToString(classString);
+      classes.Append(classString);
+      classes.AppendLiteral("'");
     }
+
+    const char* nsuri = nsid < ArrayLength(kNSURIs) ? kNSURIs[nsid] : "";
+    PR_snprintf(name, sizeof(name), "nsGenericElement%s %s%s%s %s",
+                nsuri,
+                localName.get(),
+                NS_ConvertUTF16toUTF8(id).get(),
+                NS_ConvertUTF16toUTF8(classes).get(),
+                uri.get());
     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), sizeof(nsGenericElement),
                               name);
   }
   else {
     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGenericElement, tmp->mRefCnt.get())
   }
 
   // Always need to traverse script objects, so do that before we check
--- a/content/events/src/nsDOMEventTargetHelper.cpp
+++ b/content/events/src/nsDOMEventTargetHelper.cpp
@@ -38,16 +38,17 @@
 
 #include "nsDOMEventTargetHelper.h"
 #include "nsContentUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsGUIEvent.h"
 #include "nsIDocument.h"
 #include "nsIJSContextStack.h"
 #include "nsDOMJSUtils.h"
+#include "prprf.h"
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMEventListenerWrapper)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMEventListenerWrapper)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
 NS_INTERFACE_MAP_END_AGGREGATED(mListener)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMEventListenerWrapper)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMEventListenerWrapper)
@@ -67,17 +68,31 @@ nsDOMEventListenerWrapper::HandleEvent(n
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMEventTargetHelper)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDOMEventTargetHelper)
+  if (NS_UNLIKELY(cb.WantDebugInfo())) {
+    char name[512];
+    nsAutoString uri;
+    if (tmp->mOwner && tmp->mOwner->GetExtantDocument()) {
+      tmp->mOwner->GetExtantDocument()->GetDocumentURI(uri);
+    }
+    PR_snprintf(name, sizeof(name), "nsDOMEventTargetHelper %s",
+                NS_ConvertUTF16toUTF8(uri).get());
+    cb.DescribeRefCountedNode(tmp->mRefCnt.get(), sizeof(nsDOMEventTargetHelper),
+                              name);
+  } else {
+    NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDOMEventTargetHelper, tmp->mRefCnt.get())
+  }
+
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mListenerManager,
                                                   nsEventListenerManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMEventTargetHelper)
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -261,20 +261,20 @@ AsyncClickHandler::Run()
   nsCOMPtr<nsIDocument> doc = mInput->OwnerDoc();
 
   nsPIDOMWindow* win = doc->GetWindow();
   if (!win) {
     return NS_ERROR_FAILURE;
   }
 
   // Check if page is allowed to open the popup
-  if (mPopupControlState != openAllowed) {
+  if (mPopupControlState > openControlled) {
     nsCOMPtr<nsIPopupWindowManager> pm =
       do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
- 
+
     if (!pm) {
       return NS_OK;
     }
 
     PRUint32 permission;
     pm->TestPermission(doc->GetDocumentURI(), &permission);
     if (permission == nsIPopupWindowManager::DENY_POPUP) {
       nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -482,16 +482,43 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXBLDocumentInfo)
   if (tmp->mBindingTable) {
     ProtoTracer closure = { aCallback, aClosure };
     tmp->mBindingTable->Enumerate(TraceProtos, &closure);
   }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
+static void
+UnmarkXBLJSObject(PRUint32 aLangID, void* aP, const char* aName, void* aClosure)
+{
+  if (aLangID == nsIProgrammingLanguage::JAVASCRIPT) {
+    xpc_UnmarkGrayObject(static_cast<JSObject*>(aP));
+  }
+}
+
+static bool
+UnmarkProtos(nsHashKey* aKey, void* aData, void* aClosure)
+{
+  nsXBLPrototypeBinding* proto = static_cast<nsXBLPrototypeBinding*>(aData);
+  proto->Trace(UnmarkXBLJSObject, nsnull);
+  return kHashEnumerateNext;
+}
+
+void
+nsXBLDocumentInfo::MarkInCCGeneration(PRUint32 aGeneration)
+{
+  if (mDocument) {
+    mDocument->MarkUncollectableForCCGeneration(aGeneration);
+  }
+  if (mBindingTable) {
+    mBindingTable->Enumerate(UnmarkProtos, nsnull);
+  }
+}
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXBLDocumentInfo)
   NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObjectOwner)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptGlobalObjectOwner)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXBLDocumentInfo)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXBLDocumentInfo)
--- a/content/xbl/src/nsXBLDocumentInfo.h
+++ b/content/xbl/src/nsXBLDocumentInfo.h
@@ -77,22 +77,17 @@ public:
   
   void FlushSkinStylesheets();
 
   bool IsChrome() { return mIsChrome; }
 
   // nsIScriptGlobalObjectOwner methods
   virtual nsIScriptGlobalObject* GetScriptGlobalObject();
 
-  void MarkInCCGeneration(PRUint32 aGeneration)
-  {
-    if (mDocument) {
-      mDocument->MarkUncollectableForCCGeneration(aGeneration);
-    }
-  }
+  void MarkInCCGeneration(PRUint32 aGeneration);
 
   static nsresult ReadPrototypeBindings(nsIURI* aURI, nsXBLDocumentInfo** aDocInfo);
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsXBLDocumentInfo,
                                                          nsIScriptGlobalObjectOwner)
 
 private:
   nsCOMPtr<nsIDocument> mDocument;
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -758,17 +758,19 @@ BasicThebesLayer::PaintThebes(gfxContext
       NS_ASSERTION(state.mRegionToDraw.IsEmpty(),
                    "If we need to draw, we should have a context");
     }
   }
 
   if (BasicManager()->IsTransactionIncomplete())
     return;
 
-  if (!IsHidden()) {
+  gfxRect clipExtents;
+  clipExtents = aContext->GetClipExtents();
+  if (!IsHidden() && !clipExtents.IsEmpty()) {
     AutoSetOperator setOperator(aContext, GetOperator());
     mBuffer.DrawTo(this, aContext, opacity);
   }
 
   for (PRUint32 i = 0; i < readbackUpdates.Length(); ++i) {
     ReadbackProcessor::Update& update = readbackUpdates[i];
     nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
     nsRefPtr<gfxContext> ctx =
@@ -1941,26 +1943,32 @@ BasicLayerManager::PaintLayer(gfxContext
   if (needsGroup) {
     bool blitComplete = false;
     if (is2D) {
       PopGroupToSourceWithCachedSurface(aTarget, groupTarget);
     } else {
       NS_ABORT_IF_FALSE(untransformedSurface, 
                         "We should always allocate an untransformed surface with 3d transforms!");
 
-      gfxPoint offset;
-      bool dontBlit = needsClipToVisibleRegion || mTransactionIncomplete || 
-                        aLayer->GetEffectiveOpacity() != 1.0f;
-      nsRefPtr<gfxASurface> result = 
-        Transform3D(untransformedSurface, aTarget, bounds,
-                    effectiveTransform, offset, dontBlit);
-
-      blitComplete = !result;
-      if (result) {
-        aTarget->SetSource(result, offset);
+      // Temporary fast fix for bug 725886
+      // Revert these changes when 725886 is ready
+      gfxRect clipExtents;
+      clipExtents = aTarget->GetClipExtents();
+      if (!clipExtents.IsEmpty()) {
+        gfxPoint offset;
+        bool dontBlit = needsClipToVisibleRegion || mTransactionIncomplete ||
+                          aLayer->GetEffectiveOpacity() != 1.0f;
+        nsRefPtr<gfxASurface> result =
+          Transform3D(untransformedSurface, aTarget, bounds,
+                      effectiveTransform, offset, dontBlit);
+
+        blitComplete = !result;
+        if (result) {
+          aTarget->SetSource(result, offset);
+        }
       }
     }
     // If we're doing our own double-buffering, we need to avoid drawing
     // the results of an incomplete transaction to the destination surface ---
     // that could cause flicker. Double-buffering is implemented using a
     // temporary surface for one or more container layers, so we need to stop
     // those temporary surfaces from being composited to aTarget.
     // ApplyDoubleBuffering guarantees that this container layer can't
--- a/gfx/qcms/Makefile.in
+++ b/gfx/qcms/Makefile.in
@@ -39,17 +39,16 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE       = qcms
 LIBRARY_NAME = mozqcms
 LIBXUL_LIBRARY = 1
 GRE_MODULE      = 1
-DIST_INSTALL = 1
 
 EXPORTS      = qcms.h qcmstypes.h
 
 CSRCS = \
   chain.c \
   iccread.c \
   matrix.c \
   transform.c \
--- a/image/encoders/bmp/nsBMPEncoder.cpp
+++ b/image/encoders/bmp/nsBMPEncoder.cpp
@@ -432,27 +432,36 @@ NS_IMETHODIMP nsBMPEncoder::CloseWithSta
 //
 //    Our colors are stored with premultiplied alphas, but we need
 //    an output with no alpha in machine-independent byte order.
 //
 void
 nsBMPEncoder::ConvertHostARGBRow(const PRUint8* aSrc, PRUint8* aDest,
                                  PRUint32 aPixelWidth)
 {
-  for (PRUint32 x = 0; x < aPixelWidth; x ++) {
-    const PRUint32& pixelIn = ((const PRUint32*)(aSrc))[x];
-    PRUint8 *pixelOut = &aDest[x * BytesPerPixel(mBMPInfoHeader.bpp)];
+  int bytes = BytesPerPixel(mBMPInfoHeader.bpp);
+
+  if (mBMPInfoHeader.bpp == 32) {
+    for (PRUint32 x = 0; x < aPixelWidth; x++) {
+      const PRUint32& pixelIn = ((const PRUint32*)(aSrc))[x];
+      PRUint8 *pixelOut = &aDest[x * bytes];
 
-    PRUint8 alpha = (pixelIn & 0xff000000) >> 24;
-    pixelOut[0] = (((pixelIn & 0xff0000) >> 16));
-    pixelOut[1] = (((pixelIn & 0x00ff00) >>  8));
-    pixelOut[2] = (((pixelIn & 0x0000ff) >>  0));
+      pixelOut[0] = (pixelIn & 0x00ff0000) >> 16;
+      pixelOut[1] = (pixelIn & 0x0000ff00) >>  8;
+      pixelOut[2] = (pixelIn & 0x000000ff) >>  0;
+      pixelOut[3] = (pixelIn & 0xff000000) >> 24;
+    }
+  } else {
+    for (PRUint32 x = 0; x < aPixelWidth; x++) {
+      const PRUint32& pixelIn = ((const PRUint32*)(aSrc))[x];
+      PRUint8 *pixelOut = &aDest[x * bytes];
 
-    if (mBMPInfoHeader.bpp == 32) {
-      pixelOut[3] = alpha;
+      pixelOut[0] = (pixelIn & 0xff0000) >> 16;
+      pixelOut[1] = (pixelIn & 0x00ff00) >>  8;
+      pixelOut[2] = (pixelIn & 0x0000ff) >>  0;
     }
   }
 }
 
 // nsBMPEncoder::StripAlpha
 //
 //    Input is RGBA, output is RGB
 void
--- a/image/encoders/jpeg/nsJPEGEncoder.cpp
+++ b/image/encoders/jpeg/nsJPEGEncoder.cpp
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsJPEGEncoder.h"
 #include "prmem.h"
 #include "prprf.h"
 #include "nsString.h"
 #include "nsStreamUtils.h"
+#include "gfxColor.h"
 
 #include <setjmp.h>
 #include "jerror.h"
 
 using namespace mozilla;
 
 NS_IMPL_THREADSAFE_ISUPPORTS3(nsJPEGEncoder, imgIEncoder, nsIInputStream, nsIAsyncInputStream)
 
@@ -181,17 +182,17 @@ NS_IMETHODIMP nsJPEGEncoder::InitFromDat
   if (aInputFormat == INPUT_FORMAT_RGB) {
     while (cinfo.next_scanline < cinfo.image_height) {
       const PRUint8* row = &aData[cinfo.next_scanline * aStride];
       jpeg_write_scanlines(&cinfo, const_cast<PRUint8**>(&row), 1);
     }
   } else if (aInputFormat == INPUT_FORMAT_RGBA) {
     PRUint8* row = new PRUint8[aWidth * 3];
     while (cinfo.next_scanline < cinfo.image_height) {
-      StripAlpha(&aData[cinfo.next_scanline * aStride], row, aWidth);
+      ConvertRGBARow(&aData[cinfo.next_scanline * aStride], row, aWidth);
       jpeg_write_scanlines(&cinfo, &row, 1);
     }
     delete[] row;
   } else if (aInputFormat == INPUT_FORMAT_HOSTARGB) {
     PRUint8* row = new PRUint8[aWidth * 3];
     while (cinfo.next_scanline < cinfo.image_height) {
       ConvertHostARGBRow(&aData[cinfo.next_scanline * aStride], row, aWidth);
       jpeg_write_scanlines(&cinfo, &row, 1);
@@ -345,56 +346,58 @@ NS_IMETHODIMP nsJPEGEncoder::AsyncWait(n
   return NS_OK;
 }
 
 NS_IMETHODIMP nsJPEGEncoder::CloseWithStatus(nsresult aStatus)
 {
   return Close();
 }
 
+
+
 // nsJPEGEncoder::ConvertHostARGBRow
 //
 //    Our colors are stored with premultiplied alphas, but we need
 //    an output with no alpha in machine-independent byte order.
 //
 //    See gfx/cairo/cairo/src/cairo-png.c
-
 void
 nsJPEGEncoder::ConvertHostARGBRow(const PRUint8* aSrc, PRUint8* aDest,
-                                 PRUint32 aPixelWidth)
+                                  PRUint32 aPixelWidth)
 {
-  for (PRUint32 x = 0; x < aPixelWidth; x ++) {
+  for (PRUint32 x = 0; x < aPixelWidth; x++) {
     const PRUint32& pixelIn = ((const PRUint32*)(aSrc))[x];
     PRUint8 *pixelOut = &aDest[x * 3];
 
-    pixelOut[0] = (((pixelIn & 0xff0000) >> 16));
-    pixelOut[1] = (((pixelIn & 0x00ff00) >>  8));
-    pixelOut[2] = (((pixelIn & 0x0000ff) >>  0));
+    pixelOut[0] = (pixelIn & 0xff0000) >> 16;
+    pixelOut[1] = (pixelIn & 0x00ff00) >>  8;
+    pixelOut[2] = (pixelIn & 0x0000ff) >>  0;
   }
 }
 
-
-// nsJPEGEncoder::StripAlpha
-//
-//    Input is RGBA, output is RGB
-
+/**
+ * nsJPEGEncoder::ConvertRGBARow
+ *
+ * Input is RGBA, output is RGB, so we should alpha-premultiply.
+ */
 void
-nsJPEGEncoder::StripAlpha(const PRUint8* aSrc, PRUint8* aDest,
-                          PRUint32 aPixelWidth)
+nsJPEGEncoder::ConvertRGBARow(const PRUint8* aSrc, PRUint8* aDest,
+                              PRUint32 aPixelWidth)
 {
-  for (PRUint32 x = 0; x < aPixelWidth; x ++) {
+  for (PRUint32 x = 0; x < aPixelWidth; x++) {
     const PRUint8* pixelIn = &aSrc[x * 4];
     PRUint8* pixelOut = &aDest[x * 3];
-    pixelOut[0] = pixelIn[0];
-    pixelOut[1] = pixelIn[1];
-    pixelOut[2] = pixelIn[2];
+
+    PRUint8 alpha = pixelIn[3];
+    pixelOut[0] = GFX_PREMULTIPLY(pixelIn[0], alpha);
+    pixelOut[1] = GFX_PREMULTIPLY(pixelIn[1], alpha);
+    pixelOut[2] = GFX_PREMULTIPLY(pixelIn[2], alpha);
   }
 }
 
-
 // nsJPEGEncoder::initDestination
 //
 //    Initialize destination. This is called by jpeg_start_compress() before
 //    any data is actually written. It must initialize next_output_byte and
 //    free_in_buffer. free_in_buffer must be initialized to a positive value.
 
 void // static
 nsJPEGEncoder::initDestination(jpeg_compress_struct* cinfo)
--- a/image/encoders/jpeg/nsJPEGEncoder.h
+++ b/image/encoders/jpeg/nsJPEGEncoder.h
@@ -73,17 +73,17 @@ public:
 
 private:
   ~nsJPEGEncoder();
 
 protected:
 
   void ConvertHostARGBRow(const PRUint8* aSrc, PRUint8* aDest,
                           PRUint32 aPixelWidth);
-  void StripAlpha(const PRUint8* aSrc, PRUint8* aDest, PRUint32 aPixelWidth);
+  void ConvertRGBARow(const PRUint8* aSrc, PRUint8* aDest, PRUint32 aPixelWidth);
 
   static void initDestination(jpeg_compress_struct* cinfo);
   static boolean emptyOutputBuffer(jpeg_compress_struct* cinfo);
   static void termDestination(jpeg_compress_struct* cinfo);
 
   static void errorExit(jpeg_common_struct* cinfo);
 
   void NotifyListener();
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -670,17 +670,17 @@ check-malloc-function-usage: $(filter-ou
 		"in Makefile.in" "cx->calloc_ or rt->calloc_" $^
 	$(srcdir)/config/check_source_count.py "\bjs_realloc\b" 0 \
 		"in Makefile.in" "cx->realloc_ or rt->realloc_" $^
 	$(srcdir)/config/check_source_count.py "\bjs_free\b" 0 \
 		"in Makefile.in" "cx->free_" $^
 
 	# We desire these numbers to go down, not up. See "User guide to memory
 	# management within SpiderMonkey" in jsutil.h.
-	$(srcdir)/config/check_source_count.py OffTheBooks:: 60 \
+	$(srcdir)/config/check_source_count.py OffTheBooks:: 73 \
 		"in Makefile.in" "{cx,rt}->{new_,array_new,malloc_,calloc_,realloc_}" $^
 	# This should go to zero, if possible.
 	$(srcdir)/config/check_source_count.py UnwantedForeground:: 31 \
 		"in Makefile.in" "{cx,rt}->{free_,delete_,array_delete}" $^
 
 ifneq ($(OS_ARCH),WINNT) # FIXME: this should be made work on Windows too.
 #check:: check-malloc-function-usage FIXME: disable on JM until closer to merge time.
 endif
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -33,17 +33,17 @@
  * 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 "jsinfer.h"
+#include "jscntxt.h"
 
 #include "builtin/RegExp.h"
 
 #include "vm/RegExpObject-inl.h"
 #include "vm/RegExpStatics-inl.h"
 
 using namespace js;
 using namespace js::types;
@@ -121,23 +121,23 @@ CreateRegExpMatchResult(JSContext *cx, J
         return false;
 
     *rval = ObjectValue(*array);
     return true;
 }
 
 template <class T>
 bool
-ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, T *re, JSLinearString *input,
+ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, T &re, JSLinearString *input,
                   const jschar *chars, size_t length,
                   size_t *lastIndex, RegExpExecType type, Value *rval)
 {
     LifoAllocScope allocScope(&cx->tempLifoAlloc());
     MatchPairs *matchPairs = NULL;
-    RegExpRunStatus status = re->execute(cx, chars, length, lastIndex, allocScope, &matchPairs);
+    RegExpRunStatus status = re.execute(cx, chars, length, lastIndex, &matchPairs);
 
     switch (status) {
       case RegExpRunStatus_Error:
         return false;
       case RegExpRunStatus_Success_NotFound:
         *rval = NullValue();
         return true;
       default:
@@ -154,34 +154,34 @@ ExecuteRegExpImpl(JSContext *cx, RegExpS
         *rval = BooleanValue(true);
         return true;
     }
 
     return CreateRegExpMatchResult(cx, input, chars, length, matchPairs, rval);
 }
 
 bool
-js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, const RegExpMatcher &matcher, JSLinearString *input,
+js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpShared &shared, JSLinearString *input,
                   const jschar *chars, size_t length,
                   size_t *lastIndex, RegExpExecType type, Value *rval)
 {
-    return ExecuteRegExpImpl(cx, res, &matcher, input, chars, length, lastIndex, type, rval);
+    return ExecuteRegExpImpl(cx, res, shared, input, chars, length, lastIndex, type, rval);
 }
 
 bool
-js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject *reobj, JSLinearString *input,
+js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject &reobj, JSLinearString *input,
                   const jschar *chars, size_t length,
                   size_t *lastIndex, RegExpExecType type, Value *rval)
 {
     return ExecuteRegExpImpl(cx, res, reobj, input, chars, length, lastIndex, type, rval);
 }
 
 /* Note: returns the original if no escaping need be performed. */
-static JSLinearString *
-EscapeNakedForwardSlashes(JSContext *cx, JSLinearString *unescaped)
+static JSAtom *
+EscapeNakedForwardSlashes(JSContext *cx, JSAtom *unescaped)
 {
     size_t oldLen = unescaped->length();
     const jschar *oldChars = unescaped->chars();
 
     JS::Anchor<JSString *> anchor(unescaped);
 
     /* We may never need to use |sb|. Start using it lazily. */
     StringBuffer sb(cx);
@@ -198,17 +198,17 @@ EscapeNakedForwardSlashes(JSContext *cx,
             if (!sb.append('\\'))
                 return NULL;
         }
 
         if (!sb.empty() && !sb.append(*it))
             return NULL;
     }
 
-    return sb.empty() ? unescaped : sb.finishString();
+    return sb.empty() ? unescaped : sb.finishAtom();
 }
 
 /*
  * Compile a new |RegExpShared| for the |RegExpObject|.
  *
  * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
  * arguments:
  *
@@ -225,70 +225,92 @@ CompileRegExpObject(JSContext *cx, RegEx
         RegExpObject *reobj = builder.build(cx->runtime->emptyString, res->getFlags());
         if (!reobj)
             return false;
         args.rval() = ObjectValue(*reobj);
         return true;
     }
 
     Value sourceValue = args[0];
+
+    /*
+     * If we get passed in an object whose internal [[Class]] property is
+     * "RegExp", return a new object with the same source/flags.
+     */
     if (IsObjectWithClass(sourceValue, ESClass_RegExp, cx)) {
         /*
-         * If we get passed in a |RegExpObject| source we return a new
-         * object with the same source/flags.
-         *
-         * Note: the regexp static flags are not taken into consideration here.
+         * Beware, sourceObj may be a (transparent) proxy to a RegExp, so only
+         * use generic (proxyable) operations on sourceObj that do not assume
+         * sourceObj.isRegExp().
          */
         JSObject &sourceObj = sourceValue.toObject();
+
         if (args.length() >= 2 && !args[1].isUndefined()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED);
             return false;
         }
 
-        RegExpShared *shared = RegExpToShared(cx, sourceObj);
-        if (!shared)
+        /*
+         * Only extract the 'flags' out of sourceObj; do not reuse the
+         * RegExpShared since it may be from a different compartment.
+         */
+        RegExpFlag flags;
+        {
+            RegExpShared *shared = RegExpToShared(cx, sourceObj);
+            if (!shared)
+                return false;
+
+            flags = shared->getFlags();
+        }
+
+        /*
+         * 'toSource' is a permanent read-only property, so this is equivalent
+         * to executing RegExpObject::getSource on the unwrapped object.
+         */
+        Value v;
+        if (!sourceObj.getProperty(cx, cx->runtime->atomState.sourceAtom, &v))
             return false;
 
-        shared->incref(cx);
-        RegExpObject *reobj = builder.build(AlreadyIncRefed<RegExpShared>(shared));
+        RegExpObject *reobj = builder.build(&v.toString()->asAtom(), flags);
         if (!reobj)
             return false;
 
         args.rval() = ObjectValue(*reobj);
         return true;
     }
 
-    JSLinearString *sourceStr;
+    JSAtom *source;
     if (sourceValue.isUndefined()) {
-        sourceStr = cx->runtime->emptyString;
+        source = cx->runtime->emptyString;
     } else {
         /* Coerce to string and compile. */
         JSString *str = ToString(cx, sourceValue);
         if (!str)
             return false;
-        sourceStr = str->ensureLinear(cx);
-        if (!sourceStr)
+
+        source = js_AtomizeString(cx, str);
+        if (!source)
             return false;
     }
 
     RegExpFlag flags = RegExpFlag(0);
     if (args.length() > 1 && !args[1].isUndefined()) {
         JSString *flagStr = ToString(cx, args[1]);
         if (!flagStr)
             return false;
         args[1].setString(flagStr);
         if (!ParseRegExpFlags(cx, flagStr, &flags))
             return false;
     }
 
-    JSLinearString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr);
+    JSAtom *escapedSourceStr = EscapeNakedForwardSlashes(cx, source);
     if (!escapedSourceStr)
         return false;
 
-    if (!CheckRegExpSyntax(cx, escapedSourceStr))
+    if (!js::detail::RegExpCode::checkSyntax(cx, NULL, escapedSourceStr))
         return false;
 
     RegExpStatics *res = cx->regExpStatics();
     RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags()));
     if (!reobj)
         return NULL;
 
     args.rval() = ObjectValue(*reobj);
@@ -495,16 +517,49 @@ js_InitRegExpClass(JSContext *cx, JSObje
     AddTypeProperty(cx, type, "lastIndex", Type::Int32Type());
 
     if (!DefineConstructorAndPrototype(cx, global, JSProto_RegExp, ctor, proto))
         return NULL;
 
     return proto;
 }
 
+
+static const jschar GreedyStarChars[] = {'.', '*'};
+
+static inline bool
+StartsWithGreedyStar(JSAtom *source)
+{
+    return false;
+
+#if 0
+    if (source->length() < 3)
+        return false;
+
+    const jschar *chars = source->chars();
+    return chars[0] == GreedyStarChars[0] &&
+           chars[1] == GreedyStarChars[1] &&
+           chars[2] != '?';
+#endif
+}
+
+static inline RegExpShared *
+GetSharedForGreedyStar(JSContext *cx, JSAtom *source, RegExpFlag flags)
+{
+    if (RegExpShared *hit = cx->compartment->regExps.lookupHack(cx, source, flags))
+        return hit;
+
+    JSAtom *hackedSource = js_AtomizeChars(cx, source->chars() + ArrayLength(GreedyStarChars),
+                                           source->length() - ArrayLength(GreedyStarChars));
+    if (!hackedSource)
+        return NULL;
+
+    return cx->compartment->regExps.getHack(cx, source, hackedSource, flags);
+}
+
 /*
  * ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2).
  *
  * RegExp.prototype.test doesn't need to create a results array, and we use
  * |execType| to perform this optimization.
  */
 static bool
 ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
@@ -514,27 +569,26 @@ ExecuteRegExp(JSContext *cx, Native nati
     /* Step 1. */
     bool ok;
     JSObject *obj = NonGenericMethodGuard(cx, args, native, &RegExpClass, &ok);
     if (!obj)
         return ok;
 
     RegExpObject &reobj = obj->asRegExp();
 
-    RegExpMatcher matcher(cx);
-    if (reobj.startsWithAtomizedGreedyStar()) {
-        if (!matcher.initWithTestOptimized(reobj))
-            return false;
-    } else {
-        RegExpShared *shared = reobj.getShared(cx);
-        if (!shared)
-            return false;
-        matcher.init(NeedsIncRef<RegExpShared>(shared));
-    }
+    RegExpShared *shared;
+    if (StartsWithGreedyStar(reobj.getSource()))
+        shared = GetSharedForGreedyStar(cx, reobj.getSource(), reobj.getFlags());
+    else
+        shared = reobj.getShared(cx);
 
+    if (!shared)
+        return false;
+
+    RegExpShared::Guard re(*shared);
     RegExpStatics *res = cx->regExpStatics();
 
     /* Step 2. */
     JSString *input = ToString(cx, (args.length() > 0) ? args[0] : UndefinedValue());
     if (!input)
         return false;
 
     /* Step 3. */
@@ -548,36 +602,36 @@ ExecuteRegExp(JSContext *cx, Native nati
     const Value &lastIndex = reobj.getLastIndex();
 
     /* Step 5. */
     jsdouble i;
     if (!ToInteger(cx, lastIndex, &i))
         return false;
 
     /* Steps 6-7 (with sticky extension). */
-    if (!matcher.global() && !matcher.sticky())
+    if (!re->global() && !re->sticky())
         i = 0;
 
     /* Step 9a. */
     if (i < 0 || i > length) {
         reobj.zeroLastIndex();
         args.rval() = NullValue();
         return true;
     }
 
     /* Steps 8-21. */
     RegExpExecType execType = (native == regexp_test) ? RegExpTest : RegExpExec;
     size_t lastIndexInt(i);
-    if (!ExecuteRegExp(cx, res, matcher, linearInput, chars, length, &lastIndexInt, execType,
+    if (!ExecuteRegExp(cx, res, *re, linearInput, chars, length, &lastIndexInt, execType,
                        &args.rval())) {
         return false;
     }
 
     /* Step 11 (with sticky extension). */
-    if (matcher.global() || (!args.rval().isNull() && matcher.sticky())) {
+    if (re->global() || (!args.rval().isNull() && re->sticky())) {
         if (args.rval().isNull())
             reobj.zeroLastIndex();
         else
             reobj.setLastIndex(lastIndexInt);
     }
 
     return true;
 }
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -54,22 +54,22 @@ js_InitRegExpClass(JSContext *cx, JSObje
 namespace js {
 
 /* 
  * |res| may be null if the |RegExpStatics| are not to be updated.
  * |input| may be null if there is no |JSString| corresponding to
  * |chars| and |length|.
  */
 bool
-ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject *reobj,
+ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject &reobj,
               JSLinearString *input, const jschar *chars, size_t length,
               size_t *lastIndex, RegExpExecType type, Value *rval);
 
 bool
-ExecuteRegExp(JSContext *cx, RegExpStatics *res, const RegExpMatcher &matcher,
+ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpShared &shared,
               JSLinearString *input, const jschar *chars, size_t length,
               size_t *lastIndex, RegExpExecType type, Value *rval);
 
 extern JSBool
 regexp_exec(JSContext *cx, uintN argc, Value *vp);
 
 extern JSBool
 regexp_test(JSContext *cx, uintN argc, Value *vp);
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -254,21 +254,21 @@ CodeGenerator::visitIntToString(LIntToSt
 
 bool
 CodeGenerator::visitRegExp(LRegExp *lir)
 {
     GlobalObject *global = gen->info().script()->global();
     JSObject *proto = global->getOrCreateRegExpPrototype(gen->cx);
 
     typedef JSObject *(*pf)(JSContext *, JSObject *, JSObject *);
-    static const VMFunction js_CloneRegExpObjectInfo = FunctionInfo<pf>(js_CloneRegExpObject);
+    static const VMFunction CloneRegExpObjectInfo = FunctionInfo<pf>(CloneRegExpObject);
 
     pushArg(ImmGCPtr(proto));
     pushArg(ImmGCPtr(lir->mir()->source()));
-    return callVM(js_CloneRegExpObjectInfo, lir);
+    return callVM(CloneRegExpObjectInfo, lir);
 }
 
 bool
 CodeGenerator::visitLambda(LLambda *lir)
 {
     typedef JSObject *(*pf)(JSContext *, JSFunction *, JSObject *);
     static const VMFunction Info = FunctionInfo<pf>(js::Lambda);
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug713226.js
@@ -0,0 +1,21 @@
+// |jit-test| mjitalways;
+gczeal(4);
+var optionNames = options().split(',');
+  for (var i = 0; i < optionNames.length; i++)
+    var optionName = optionNames[i];
+      options(optionName);
+evaluate("\
+function addDebug(g, id) {\
+    var debuggerGlobal = newGlobal('new-compartment');\
+    debuggerGlobal.debuggee = g;\
+    debuggerGlobal.id = id;\
+    debuggerGlobal.print = function (s) { (g) += s; };\
+    debuggerGlobal.eval('var dbg = new Debugger(debuggee);dbg.onDebuggerStatement = function () { print(id); debugger; };');\
+    return debuggerGlobal;\
+}\
+var base = newGlobal('new-compartment');\
+var top = base;\
+for (var i = 0; i < 8; i++ )\
+    top = addDebug(top, i);\
+base.eval('debugger;');\
+");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug722028.js
@@ -0,0 +1,13 @@
+
+gczeal(4);
+var BUGNUMBER = 668024;
+var summary =
+print(BUGNUMBER + ": " + summary);
+var arr = [0, 1, 2, 3, 4, 5, , 7];
+var seen = [];
+for (var p in arr) {
+    if (seen.indexOf(unescape) >= 0) {}
+    arr.splice(2, 3);
+  seen.push(p);
+}
+
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -939,22 +939,25 @@ ScriptAnalysis::killVariable(JSContext *
      */
     var.lifetime->start = offset;
     var.lifetime->write = true;
 
     if (var.ensured) {
         /*
          * The variable is live even before the write, due to an enclosing try
          * block. We need to split the lifetime to indicate there was a write.
+         * We set the new interval's savedEnd to 0, since it will always be
+         * adjacent to the old interval, so it never needs to be extended.
          */
-        var.lifetime = cx->typeLifoAlloc().new_<Lifetime>(start, offset, var.lifetime);
+        var.lifetime = cx->typeLifoAlloc().new_<Lifetime>(start, 0, var.lifetime);
         if (!var.lifetime) {
             setOOM(cx);
             return;
         }
+        var.lifetime->end = offset;
     } else {
         var.saved = var.lifetime;
         var.savedEnd = 0;
         var.lifetime = NULL;
 
         saved[savedCount++] = &var;
     }
 }
@@ -972,35 +975,53 @@ ScriptAnalysis::extendVariable(JSContext
          */
         JS_ASSERT(var.lifetime->start < start);
         return;
     }
 
     var.lifetime->start = start;
 
     /*
-     * When walking backwards through loop bodies, we don't know which vars
-     * are live at the loop's backedge. We save the endpoints for lifetime
-     * segments which we *would* use if the variables were live at the backedge
-     * and extend the variable with new lifetimes if we find the variable is
-     * indeed live at the head of the loop.
+     * Consider this code:
+     *
+     *   while (...) { (#1)
+     *       use x;    (#2)
+     *       ...
+     *       x = ...;  (#3)
+     *       ...
+     *   }             (#4)
+     *
+     * Just before analyzing the while statement, there would be a live range
+     * from #1..#2 and a "point range" at #3. The job of extendVariable is to
+     * create a new live range from #3..#4.
+     *
+     * However, more extensions may be required if the definition of x is
+     * conditional. Consider the following.
      *
-     * while (...) {
-     *   if (x #1) { ... }
-     *   ...
-     *   if (... #2) { x = 0; #3}
-     * }
+     *   while (...) {     (#1)
+     *       use x;        (#2)
+     *       ...
+     *       if (...)      (#5)
+     *           x = ...;  (#3)
+     *       ...
+     *   }                 (#4)
      *
-     * If x is not live after the loop, we treat it as dead in the walk and
-     * make a point lifetime for the write at #3. At the beginning of that
-     * basic block (#2), we save the loop endpoint; if we knew x was live in
-     * the next iteration then a new lifetime would be made here. At #1 we
-     * mark x live again, make a segment between the head of the loop and #1,
-     * and then extend x with loop tail lifetimes from #1 to #2, and from #3
-     * to the back edge.
+     * Assume that x is not used after the loop. Then, before extendVariable is
+     * run, the live ranges would be the same as before (#1..#2 and #3..#3). We
+     * still need to create a range from #3..#4. But, since the assignment at #3
+     * may never run, we also need to create a range from #2..#3. This is done
+     * as follows.
+     *
+     * Each time we create a Lifetime, we store the start of the most recently
+     * seen sequence of conditional code in the Lifetime's savedEnd field. So,
+     * when creating the Lifetime at #2, we set the Lifetime's savedEnd to
+     * #5. (The start of the most recent conditional is cached in each
+     * variable's savedEnd field.) Consequently, extendVariable is able to
+     * create a new interval from #2..#5 using the savedEnd field of the
+     * existing #1..#2 interval.
      */
 
     Lifetime *segment = var.lifetime;
     while (segment && segment->start < end) {
         uint32_t savedEnd = segment->savedEnd;
         if (!segment->next || segment->next->start >= end) {
             /*
              * savedEnd is only set for variables killed in the middle of the
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -701,17 +701,16 @@ static JSBool js_NewRuntimeWasCalled = J
 JSRuntime::JSRuntime()
   : atomsCompartment(NULL),
 #ifdef JS_THREADSAFE
     ownerThread_(NULL),
 #endif
     tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     execAlloc_(NULL),
     bumpAlloc_(NULL),
-    reCache_(NULL),
     nativeStackBase(0),
     nativeStackQuota(0),
     interpreterFrames(NULL),
     cxCallback(NULL),
     compartmentCallback(NULL),
     activityCallback(NULL),
     activityCallbackArg(NULL),
 #ifdef JS_THREADSAFE
@@ -720,18 +719,16 @@ JSRuntime::JSRuntime()
 # ifdef DEBUG
     checkRequestDepth(0),
 # endif
 #endif
     gcSystemAvailableChunkListHead(NULL),
     gcUserAvailableChunkListHead(NULL),
     gcKeepAtoms(0),
     gcBytes(0),
-    gcTriggerBytes(0),
-    gcLastBytes(0),
     gcMaxBytes(0),
     gcMaxMallocBytes(0),
     gcNumArenasFreeCommitted(0),
     gcNumber(0),
     gcIncrementalTracer(NULL),
     gcVerifyData(NULL),
     gcChunkAllocationSinceLastGC(false),
     gcNextFullGCTime(0),
@@ -853,17 +850,16 @@ JSRuntime::init(uint32_t maxbytes)
 }
 
 JSRuntime::~JSRuntime()
 {
     JS_ASSERT(onOwnerThread());
 
     delete_<JSC::ExecutableAllocator>(execAlloc_);
     delete_<WTF::BumpPointerAllocator>(bumpAlloc_);
-    JS_ASSERT(!reCache_);
 
 #ifdef DEBUG
     /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */
     if (!JS_CLIST_IS_EMPTY(&contextList)) {
         JSContext *cx, *iter = NULL;
         uintN cxcount = 0;
         while ((cx = js_ContextIterator(this, JS_TRUE, &iter)) != NULL) {
             fprintf(stderr,
@@ -6374,17 +6370,17 @@ JS_ClearRegExpStatics(JSContext *cx, JSO
 JS_PUBLIC_API(JSBool)
 JS_ExecuteRegExp(JSContext *cx, JSObject *obj, JSObject *reobj, jschar *chars, size_t length,
                  size_t *indexp, JSBool test, jsval *rval)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
 
     RegExpStatics *res = obj->asGlobal().getRegExpStatics();
-    return ExecuteRegExp(cx, res, &reobj->asRegExp(), NULL, chars, length,
+    return ExecuteRegExp(cx, res, reobj->asRegExp(), NULL, chars, length,
                          indexp, test ? RegExpTest : RegExpExec, rval);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewRegExpObjectNoStatics(JSContext *cx, char *bytes, size_t length, uintN flags)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
@@ -6406,17 +6402,17 @@ JS_NewUCRegExpObjectNoStatics(JSContext 
 
 JS_PUBLIC_API(JSBool)
 JS_ExecuteRegExpNoStatics(JSContext *cx, JSObject *obj, jschar *chars, size_t length,
                           size_t *indexp, JSBool test, jsval *rval)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
 
-    return ExecuteRegExp(cx, NULL, &obj->asRegExp(), NULL, chars, length, indexp,
+    return ExecuteRegExp(cx, NULL, obj->asRegExp(), NULL, chars, length, indexp,
                          test ? RegExpTest : RegExpExec, rval);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ObjectIsRegExp(JSContext *cx, JSObject *obj)
 {
     AssertNoGC(cx);
     JS_ASSERT(obj);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -143,33 +143,16 @@ JSRuntime::createBumpPointerAllocator(JS
     JS_ASSERT(cx->runtime == this);
 
     bumpAlloc_ = new_<WTF::BumpPointerAllocator>();
     if (!bumpAlloc_)
         js_ReportOutOfMemory(cx);
     return bumpAlloc_;
 }
 
-RegExpCache *
-JSRuntime::createRegExpCache(JSContext *cx)
-{
-    JS_ASSERT(!reCache_);
-    JS_ASSERT(cx->runtime == this);
-
-    RegExpCache *newCache = new_<RegExpCache>(this);
-    if (!newCache || !newCache->init()) {
-        js_ReportOutOfMemory(cx);
-        delete_<RegExpCache>(newCache);
-        return NULL;
-    }
-
-    reCache_ = newCache;
-    return reCache_;
-}
-
 JSScript *
 js_GetCurrentScript(JSContext *cx)
 {
     return cx->hasfp() ? cx->fp()->maybeScript() : NULL;
 }
 
 JSContext *
 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
@@ -1165,19 +1148,16 @@ JSRuntime::onOutOfMemory(void *p, size_t
 void
 JSRuntime::purge(JSContext *cx)
 {
     tempLifoAlloc.freeUnused();
     gsnCache.purge();
 
     /* FIXME: bug 506341 */
     propertyCache.purge(cx);
-
-    delete_<RegExpCache>(reCache_);
-    reCache_ = NULL;
 }
 
 void
 JSContext::purge()
 {
     if (!activeCompilations) {
         Foreground::delete_<ParseMapPool>(parseMapPool_);
         parseMapPool_ = NULL;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -95,21 +95,16 @@ class IonActivation;
 }
 
 class WeakMapBase;
 class InterpreterFrames;
 
 class ScriptOpcodeCounts;
 struct ScriptOpcodeCountsPair;
 
-typedef HashMap<JSAtom *,
-                detail::RegExpCacheValue,
-                DefaultHasher<JSAtom *>,
-                RuntimeAllocPolicy> RegExpCache;
-
 /*
  * GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
  * given pc in a script. We use the script->code pointer to tag the cache,
  * instead of the script address itself, so that source notes are always found
  * by offset from the bytecode with which they were generated.
  */
 struct GSNCache {
     typedef HashMap<jsbytecode *,
@@ -219,35 +214,27 @@ struct JSRuntime : js::RuntimeFriendFiel
 
   private:
     /*
      * Both of these allocators are used for regular expression code which is shared at the
      * thread-data level.
      */
     JSC::ExecutableAllocator *execAlloc_;
     WTF::BumpPointerAllocator *bumpAlloc_;
-    js::RegExpCache *reCache_;
 
     JSC::ExecutableAllocator *createExecutableAllocator(JSContext *cx);
     WTF::BumpPointerAllocator *createBumpPointerAllocator(JSContext *cx);
-    js::RegExpCache *createRegExpCache(JSContext *cx);
 
   public:
     JSC::ExecutableAllocator *getExecutableAllocator(JSContext *cx) {
         return execAlloc_ ? execAlloc_ : createExecutableAllocator(cx);
     }
     WTF::BumpPointerAllocator *getBumpPointerAllocator(JSContext *cx) {
         return bumpAlloc_ ? bumpAlloc_ : createBumpPointerAllocator(cx);
     }
-    js::RegExpCache *maybeRegExpCache() {
-        return reCache_;
-    }
-    js::RegExpCache *getRegExpCache(JSContext *cx) {
-        return reCache_ ? reCache_ : createRegExpCache(cx);
-    }
 
     /* Base address of the native stack for the current thread. */
     uintptr_t           nativeStackBase;
 
     /* The native stack size limit that runtime should not exceed. */
     size_t              nativeStackQuota;
 
     /*
@@ -296,18 +283,16 @@ struct JSRuntime : js::RuntimeFriendFiel
     js::gc::Chunk       *gcSystemAvailableChunkListHead;
     js::gc::Chunk       *gcUserAvailableChunkListHead;
     js::gc::ChunkPool   gcChunkPool;
 
     js::RootedValueMap  gcRootsHash;
     js::GCLocks         gcLocksHash;
     jsrefcount          gcKeepAtoms;
     size_t              gcBytes;
-    size_t              gcTriggerBytes;
-    size_t              gcLastBytes;
     size_t              gcMaxBytes;
     size_t              gcMaxMallocBytes;
 
     /*
      * Number of the committed arenas in all GC chunks including empty chunks.
      * The counter is volatile as it is read without the GC lock, see comments
      * in MaybeGC.
      */
@@ -561,19 +546,16 @@ struct JSRuntime : js::RuntimeFriendFiel
 
     JSRuntime();
     ~JSRuntime();
 
     bool init(uint32_t maxbytes);
 
     JSRuntime *thisFromCtor() { return this; }
 
-    void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind);
-    void reduceGCTriggerBytes(size_t amount);
-
     /*
      * Call the system malloc while checking for GC memory pressure and
      * reporting OOM error when cx is not null. We will not GC from here.
      */
     void* malloc_(size_t bytes, JSContext *cx = NULL) {
         updateMallocCounter(cx, bytes);
         void *p = ::js_malloc(bytes);
         return JS_LIKELY(!!p) ? p : onOutOfMemory(NULL, bytes, cx);
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -81,16 +81,17 @@ JSCompartment::JSCompartment(JSRuntime *
     hold(false),
     typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     data(NULL),
     active(false),
     hasDebugModeCodeToDrop(false),
 #ifdef JS_METHODJIT
     jaegerCompartment_(NULL),
 #endif
+    regExps(rt),
     propertyTree(thisForCtor()),
     emptyTypeObject(NULL),
     debugModeBits(rt->debugMode ? DebugFromC : 0),
     mathCache(NULL),
 	watchpointMap(NULL)
 #ifdef JS_ION
     , ionCompartment_(NULL)
 #endif
@@ -124,16 +125,19 @@ JSCompartment::init(JSContext *cx)
     activeAnalysis = activeInference = false;
     types.init(cx);
 
     newObjectCache.reset();
 
     if (!crossCompartmentWrappers.init())
         return false;
 
+    if (!regExps.init(cx))
+        return false;
+
     if (!scriptFilenameTable.init())
         return false;
 
     return debuggees.init();
 }
 
 #ifdef JS_ION
 bool
@@ -570,16 +574,17 @@ JSCompartment::sweep(JSContext *cx, bool
 
     active = false;
 }
 
 void
 JSCompartment::purge(JSContext *cx)
 {
     arenas.purge();
+    regExps.purge();
     dtoaCache.purge();
 
     /*
      * Clear the hash and reset all evalHashLink to null before the GC. This
      * way MarkChildren(trc, JSScript *) can assume that JSScript::u.object is
      * not null when we have script owned by an object and not from the eval
      * cache.
      */
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -45,16 +45,17 @@
 #include "jsclist.h"
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsgcstats.h"
 #include "jsobj.h"
 #include "jsscope.h"
 #include "vm/GlobalObject.h"
+#include "vm/RegExpObject.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4251) /* Silence warning about JS_FRIEND_API and data members. */
 #endif
 
 namespace js {
 
@@ -243,16 +244,18 @@ struct JSCompartment
         return jaegerCompartment_;
     }
 
     bool ensureJaegerCompartmentExists(JSContext *cx);
 
     size_t sizeOfMjitCode() const;
 #endif
 
+    js::RegExpCompartment        regExps;
+
     size_t sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf);
     void sizeOfTypeInferenceData(JSContext *cx, JS::TypeInferenceSizes *stats,
                                  JSMallocSizeOfFun mallocSizeOf);
 
     /*
      * Shared scope property tree, and arena-pool for allocating its nodes.
      */
     js::PropertyTree             propertyTree;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -790,20 +790,18 @@ Chunk::releaseArena(ArenaHeader *aheader
     if (rt->gcHelperThread.sweeping())
         maybeLock.lock(rt);
 #endif
 
     Probes::resizeHeap(comp, rt->gcBytes, rt->gcBytes - ArenaSize);
     JS_ASSERT(rt->gcBytes >= ArenaSize);
     JS_ASSERT(comp->gcBytes >= ArenaSize);
 #ifdef JS_THREADSAFE
-    if (rt->gcHelperThread.sweeping()) {
-        rt->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize);
+    if (rt->gcHelperThread.sweeping())
         comp->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize);
-    }
 #endif
     rt->gcBytes -= ArenaSize;
     comp->gcBytes -= ArenaSize;
 
     aheader->setAsNotAllocated();
     addArenaToFreeList(rt, aheader);
 
     if (info.numArenasFree == 1) {
@@ -899,22 +897,16 @@ js_InitGC(JSRuntime *rt, uint32_t maxbyt
 
     /*
      * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
      * for default backward API compatibility.
      */
     rt->gcMaxBytes = maxbytes;
     rt->setGCMaxMallocBytes(maxbytes);
 
-    /*
-     * The assigned value prevents GC from running when GC memory is too low
-     * (during JS engine start).
-     */
-    rt->setGCLastBytes(8192, GC_NORMAL);
-
     rt->gcJitReleaseTime = PRMJ_Now() + JIT_SCRIPT_RELEASE_TYPES_INTERVAL;
     return true;
 }
 
 namespace js {
 
 inline bool
 InFreeList(ArenaHeader *aheader, uintptr_t addr)
@@ -1389,46 +1381,28 @@ js_MapGCRoots(JSRuntime *rt, JSGCRootMap
         if (mapflags & JS_MAP_GCROOT_STOP)
             break;
     }
 
     return ct;
 }
 
 void
-JSRuntime::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
-{
-    gcLastBytes = lastBytes;
-
-    size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ALLOCATION_THRESHOLD);
-    float trigger = float(base) * GC_HEAP_GROWTH_FACTOR;
-    gcTriggerBytes = size_t(Min(float(gcMaxBytes), trigger));
-}
-
-void
-JSRuntime::reduceGCTriggerBytes(size_t amount) {
-    JS_ASSERT(amount > 0);
-    JS_ASSERT(gcTriggerBytes - amount >= 0);
-    if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR)
-        return;
-    gcTriggerBytes -= amount;
-}
-
-void
 JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
 {
     gcLastBytes = lastBytes;
 
     size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ALLOCATION_THRESHOLD);
     float trigger = float(base) * GC_HEAP_GROWTH_FACTOR;
     gcTriggerBytes = size_t(Min(float(rt->gcMaxBytes), trigger));
 }
 
 void
-JSCompartment::reduceGCTriggerBytes(size_t amount) {
+JSCompartment::reduceGCTriggerBytes(size_t amount)
+{
     JS_ASSERT(amount > 0);
     JS_ASSERT(gcTriggerBytes - amount >= 0);
     if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR)
         return;
     gcTriggerBytes -= amount;
 }
 
 namespace js {
@@ -1931,36 +1905,16 @@ gc_root_traversal(JSTracer *trc, const R
 static void
 gc_lock_traversal(const GCLocks::Entry &entry, JSTracer *trc)
 {
     JS_ASSERT(entry.value >= 1);
     MarkRootGCThing(trc, entry.key, "locked object");
 }
 
 void
-js_TraceStackFrame(JSTracer *trc, StackFrame *fp)
-{
-    MarkRoot(trc, &fp->scopeChain(), "scope chain");
-    if (fp->isDummyFrame())
-        return;
-    if (fp->hasArgsObj())
-        MarkRoot(trc, &fp->argsObj(), "arguments");
-    if (fp->isFunctionFrame()) {
-        MarkRoot(trc, fp->fun(), "fun");
-        if (fp->isEvalFrame()) {
-            MarkRoot(trc, fp->script(), "eval script");
-        }
-    } else {
-        MarkRoot(trc, fp->script(), "script");
-    }
-    fp->script()->compartment()->active = true;
-    MarkRoot(trc, fp->returnValue(), "rval");
-}
-
-void
 AutoIdArray::trace(JSTracer *trc)
 {
     JS_ASSERT(tag == IDARRAY);
     gc::MarkIdRange(trc, idArray->vector, idArray->vector + idArray->length,
                     "JSAutoIdArray.idArray");
 }
 
 void
@@ -2167,16 +2121,22 @@ MarkRuntime(JSTracer *trc)
             for (CellIterUnderGC i(c, FINALIZE_SCRIPT); !i.done(); i.next()) {
                 JSScript *script = i.get<JSScript>();
                 if (script->pcCounters)
                     MarkRoot(trc, script, "profilingScripts");
             }
         }
     }
 
+#ifdef JS_METHODJIT
+    /* We need to expand inline frames before stack scanning. */
+    for (CompartmentsIter c(rt); !c.done(); c.next())
+        mjit::ExpandInlineFrames(c);
+#endif
+
     rt->stackSpace.mark(trc);
 #ifdef JS_ION
     ion::MarkIonActivations(rt, trc);
 #endif
 
     for (GCCompartmentsIter c(rt); !c.done(); c.next())
         c->mark(trc);
 
@@ -2225,22 +2185,16 @@ TriggerCompartmentGC(JSCompartment *comp
 
     if (rt->gcIsNeeded) {
         /* If we need to GC more than one compartment, run a full GC. */
         if (rt->gcTriggerCompartment != comp)
             rt->gcTriggerCompartment = NULL;
         return;
     }
 
-    if (rt->gcBytes > 8192 && rt->gcBytes >= 3 * (rt->gcTriggerBytes / 2)) {
-        /* If we're using significantly more than our quota, do a full GC. */
-        TriggerGC(rt, reason);
-        return;
-    }
-
     /*
      * Trigger the GC when it is safe to call an operation callback on any
      * thread.
      */
     rt->gcIsNeeded = true;
     rt->gcTriggerCompartment = comp;
     rt->gcTriggerReason = reason;
     comp->rt->triggerOperationCallback();
@@ -3024,17 +2978,16 @@ GCCycle(JSContext *cx, JSCompartment *co
     if (cx->gcBackgroundFree) {
         JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
         cx->gcBackgroundFree = NULL;
         rt->gcHelperThread.startBackgroundSweep(cx, gckind == GC_SHRINK);
     }
 #endif
 
     rt->gcMarkAndSweep = false;
-    rt->setGCLastBytes(rt->gcBytes, gckind);
     rt->gcCurrentCompartment = NULL;
 
     for (CompartmentsIter c(rt); !c.done(); c.next())
         c->setGCLastBytes(c->gcBytes, gckind);
 }
 
 void
 js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, gcreason::Reason reason)
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1371,19 +1371,16 @@ js_LockGCThingRT(JSRuntime *rt, void *th
 extern void
 js_UnlockGCThingRT(JSRuntime *rt, void *thing);
 
 extern JS_FRIEND_API(bool)
 IsAboutToBeFinalized(const js::gc::Cell *thing);
 
 extern bool
 IsAboutToBeFinalized(const js::Value &value);
-extern void
-js_TraceStackFrame(JSTracer *trc, js::StackFrame *fp);
-
 extern bool
 js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, js::gc::AllocKind *thingKind, void **thing);
 
 namespace js {
 
 extern void
 TraceRuntime(JSTracer *trc);
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2945,17 +2945,17 @@ BEGIN_CASE(JSOP_REGEXP)
     /*
      * Push a regexp object cloned from the regexp literal object mapped by the
      * bytecode at pc.
      */
     uint32_t index = GET_UINT32_INDEX(regs.pc);
     JSObject *proto = regs.fp()->scopeChain().global().getOrCreateRegExpPrototype(cx);
     if (!proto)
         goto error;
-    JSObject *obj = js_CloneRegExpObject(cx, script->getRegExp(index), proto);
+    JSObject *obj = CloneRegExpObject(cx, script->getRegExp(index), proto);
     if (!obj)
         goto error;
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_REGEXP)
 
 BEGIN_CASE(JSOP_ZERO)
     PUSH_INT32(0);
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1033,16 +1033,22 @@ SuppressDeletedPropertyHelper(JSContext 
                      * If it is the next property to be enumerated, just skip it.
                      */
                     if (idp == props_cursor) {
                         ni->incCursor();
                     } else {
                         for (HeapPtr<JSFlatString> *p = idp; p + 1 != props_end; p++)
                             *p = *(p + 1);
                         ni->props_end = ni->end() - 1;
+
+                        /*
+                         * Invoke the write barrier on this element, since it's
+                         * no longer going to be marked.
+                         */
+                        ni->props_end->HeapPtr<JSFlatString>::~HeapPtr<JSFlatString>();
                     }
 
                     /* Don't reuse modified native iterators. */
                     ni->flags |= JSITER_UNREUSABLE;
 
                     if (predicate.matchesAtMostOne())
                         break;
                 }
@@ -1383,17 +1389,17 @@ MarkGenerator(JSTracer *trc, JSGenerator
     /*
      * Currently, generators are not mjitted. Still, (overflow) args can be
      * pushed by the mjit and need to be conservatively marked. Technically, the
      * formal args and generator slots are safe for exact marking, but since the
      * plan is to eventually mjit generators, it makes sense to future-proof
      * this code and save someone an hour later.
      */
     MarkStackRangeConservatively(trc, gen->floatingStack, fp->formalArgsEnd());
-    js_TraceStackFrame(trc, fp);
+    fp->mark(trc);
     MarkStackRangeConservatively(trc, fp->slots(), gen->regs.sp);
 }
 
 static void
 GeneratorWriteBarrierPre(JSContext *cx, JSGenerator *gen)
 {
     JSCompartment *comp = cx->compartment;
     if (comp->needsBarrier())
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -119,28 +119,22 @@ class JSAtom;
 class JSWrapper;
 
 namespace js {
 
 struct ArgumentsData;
 struct Class;
 
 class RegExpObject;
-class RegExpMatcher;
 class RegExpObjectBuilder;
 class RegExpShared;
 class RegExpStatics;
 class MatchPairs;
 
-namespace detail {
-
-class RegExpCode;
-class RegExpCacheValue;
-
-} /* namespace detail */
+namespace detail { class RegExpCode; }
 
 enum RegExpFlag
 {
     IgnoreCaseFlag  = 0x01,
     GlobalFlag      = 0x02,
     MultilineFlag   = 0x04,
     StickyFlag      = 0x08,
 
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -2815,17 +2815,17 @@ ASTSerializer::literal(ParseNode *pn, Va
       {
         JSObject *re1 = pn->pn_objbox ? pn->pn_objbox->object : NULL;
         LOCAL_ASSERT(re1 && re1->isRegExp());
 
         JSObject *proto;
         if (!js_GetClassPrototype(cx, &cx->fp()->scopeChain(), JSProto_RegExp, &proto))
             return false;
 
-        JSObject *re2 = js_CloneRegExpObject(cx, re1, proto);
+        JSObject *re2 = CloneRegExpObject(cx, re1, proto);
         if (!re2)
             return false;
 
         val.setObject(*re2);
         break;
       }
 
       case PNK_NUMBER:
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1327,97 +1327,123 @@ str_trimRight(JSContext *cx, uintN argc,
 
 /*
  * Perl-inspired string functions.
  */
 
 /* Result of a successfully performed flat match. */
 class FlatMatch
 {
-    JSLinearString  *patstr;
-    const jschar    *pat;
-    size_t          patlen;
-    int32_t         match_;
+    JSAtom       *patstr;
+    const jschar *pat;
+    size_t       patlen;
+    int32_t      match_;
 
     friend class RegExpGuard;
 
   public:
     FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */
     JSLinearString *pattern() const { return patstr; }
     size_t patternLength() const { return patlen; }
 
     /*
      * Note: The match is -1 when the match is performed successfully,
      * but no match is found.
      */
     int32_t match() const { return match_; }
 };
 
+static inline bool
+IsRegExpMetaChar(jschar c)
+{
+    switch (c) {
+      /* Taken from the PatternCharacter production in 15.10.1. */
+      case '^': case '$': case '\\': case '.': case '*': case '+':
+      case '?': case '(': case ')': case '[': case ']': case '{':
+      case '}': case '|':
+        return true;
+      default:
+        return false;
+    }
+}
+
+static inline bool
+HasRegExpMetaChars(const jschar *chars, size_t length)
+{
+    for (size_t i = 0; i < length; ++i) {
+        if (IsRegExpMetaChar(chars[i]))
+            return true;
+    }
+    return false;
+}
+
 /*
  * RegExpGuard factors logic out of String regexp operations.
  *
  * |optarg| indicates in which argument position RegExp flags will be found, if
  * present. This is a Mozilla extension and not part of any ECMA spec.
  */
 class RegExpGuard
 {
     RegExpGuard(const RegExpGuard &) MOZ_DELETE;
     void operator=(const RegExpGuard &) MOZ_DELETE;
 
-    JSContext     *cx;
-    RegExpMatcher matcher;
-    FlatMatch     fm;
+    RegExpShared::Guard re_;
+    FlatMatch           fm;
 
     /*
      * Upper bound on the number of characters we are willing to potentially
      * waste on searching for RegExp meta-characters.
      */
     static const size_t MAX_FLAT_PAT_LEN = 256;
 
-    static JSLinearString *flattenPattern(JSContext *cx, JSLinearString *patstr) {
+    static JSAtom *
+    flattenPattern(JSContext *cx, JSAtom *patstr)
+    {
         StringBuffer sb(cx);
         if (!sb.reserve(patstr->length()))
             return NULL;
 
         static const jschar ESCAPE_CHAR = '\\';
         const jschar *chars = patstr->chars();
         size_t len = patstr->length();
         for (const jschar *it = chars; it != chars + len; ++it) {
             if (IsRegExpMetaChar(*it)) {
                 if (!sb.append(ESCAPE_CHAR) || !sb.append(*it))
                     return NULL;
             } else {
                 if (!sb.append(*it))
                     return NULL;
             }
         }
-        return sb.finishString();
+        return sb.finishAtom();
     }
 
   public:
-    explicit RegExpGuard(JSContext *cx) : cx(cx), matcher(cx) {}
-    ~RegExpGuard() {}
+    RegExpGuard() {}
 
     /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
-    bool
-    init(CallArgs args, bool convertVoid = false)
+    bool init(JSContext *cx, CallArgs args, bool convertVoid = false)
     {
         if (args.length() != 0 && IsObjectWithClass(args[0], ESClass_RegExp, cx)) {
             RegExpShared *shared = RegExpToShared(cx, args[0].toObject());
             if (!shared)
                 return false;
-
-            matcher.init(NeedsIncRef<RegExpShared>(shared));
+            re_.init(*shared);
         } else {
             if (convertVoid && (args.length() == 0 || args[0].isUndefined())) {
                 fm.patstr = cx->runtime->emptyString;
                 return true;
             }
 
-            fm.patstr = ArgToRootedString(cx, args, 0);
+            JSString *arg = ArgToRootedString(cx, args, 0);
+            if (!arg)
+                return false;
+
+            fm.patstr = js_AtomizeString(cx, arg);
             if (!fm.patstr)
                 return false;
         }
         return true;
     }
 
     /*
      * Attempt to match |patstr| to |textstr|. A flags argument, metachars in the
@@ -1428,17 +1454,17 @@ class RegExpGuard
      * Return whether flat matching could be used.
      *
      * N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->isExceptionPending().
      */
     const FlatMatch *
     tryFlatMatch(JSContext *cx, JSString *textstr, uintN optarg, uintN argc,
                  bool checkMetaChars = true)
     {
-        if (matcher.initialized())
+        if (re_.initialized())
             return NULL;
 
         fm.pat = fm.patstr->chars();
         fm.patlen = fm.patstr->length();
 
         if (optarg < argc)
             return NULL;
 
@@ -1458,51 +1484,50 @@ class RegExpGuard
             const jschar *text = textstr->asLinear().chars();
             size_t textlen = textstr->length();
             fm.match_ = StringMatch(text, textlen, fm.pat, fm.patlen);
         }
         return &fm;
     }
 
     /* If the pattern is not already a regular expression, make it so. */
-    const RegExpMatcher *
-    normalizeRegExp(bool flat, uintN optarg, CallArgs args)
+    bool normalizeRegExp(JSContext *cx, bool flat, uintN optarg, CallArgs args)
     {
-        if (matcher.initialized())
-            return &matcher;
+        if (re_.initialized())
+            return true;
 
         /* Build RegExp from pattern string. */
         JSString *opt;
         if (optarg < args.length()) {
             opt = ToString(cx, args[optarg]);
             if (!opt)
-                return NULL;
+                return false;
         } else {
             opt = NULL;
         }
 
-        JSLinearString *patstr;
+        JSAtom *patstr;
         if (flat) {
             patstr = flattenPattern(cx, fm.patstr);
             if (!patstr)
-                return NULL;
+                return false;
         } else {
             patstr = fm.patstr;
         }
         JS_ASSERT(patstr);
 
-        if (!matcher.init(patstr, opt))
-            return NULL;
-
-        return &matcher;
+        RegExpShared *re = cx->compartment->regExps.get(cx, patstr, opt);
+        if (!re)
+            return false;
+
+        re_.init(*re);
+        return true;
     }
 
-#if DEBUG
-    bool matcherInitialized() const { return matcher.initialized(); }
-#endif
+    RegExpShared &regExp() { return *re_; }
 };
 
 /* ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
 static JS_ALWAYS_INLINE bool
 Matched(RegExpExecType type, const Value &v)
 {
     return (type == RegExpTest) ? v.isTrue() : !v.isNull();
 }
@@ -1520,43 +1545,43 @@ enum MatchControlFlags {
 
    MATCH_ARGS    = TEST_GLOBAL_BIT,
    MATCHALL_ARGS = CALLBACK_ON_SINGLE_BIT,
    REPLACE_ARGS  = TEST_GLOBAL_BIT | TEST_SINGLE_BIT | CALLBACK_ON_SINGLE_BIT
 };
 
 /* Factor out looping and matching logic. */
 static bool
-DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpMatcher &matcher,
+DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, RegExpShared &re,
         DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval)
 {
     JSLinearString *linearStr = str->ensureLinear(cx);
     if (!linearStr)
         return false;
 
     const jschar *chars = linearStr->chars();
     size_t length = linearStr->length();
 
-    if (matcher.global()) {
+    if (re.global()) {
         RegExpExecType type = (flags & TEST_GLOBAL_BIT) ? RegExpTest : RegExpExec;
         for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
-            if (!ExecuteRegExp(cx, res, matcher, linearStr, chars, length, &i, type, rval))
+            if (!ExecuteRegExp(cx, res, re, linearStr, chars, length, &i, type, rval))
                 return false;
             if (!Matched(type, *rval))
                 break;
             if (!callback(cx, res, count, data))
                 return false;
             if (!res->matched())
                 ++i;
         }
     } else {
         RegExpExecType type = (flags & TEST_SINGLE_BIT) ? RegExpTest : RegExpExec;
         bool callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
         size_t i = 0;
-        if (!ExecuteRegExp(cx, res, matcher, linearStr, chars, length, &i, type, rval))
+        if (!ExecuteRegExp(cx, res, re, linearStr, chars, length, &i, type, rval))
             return false;
         if (callbackOnSingle && Matched(type, *rval) && !callback(cx, res, 0, data))
             return false;
     }
     return true;
 }
 
 static bool
@@ -1608,93 +1633,91 @@ MatchCallback(JSContext *cx, RegExpStati
 JSBool
 js::str_match(JSContext *cx, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JSString *str = ThisToStringForStringProto(cx, args);
     if (!str)
         return false;
 
-    RegExpGuard g(cx);
-    if (!g.init(args, true))
+    RegExpGuard g;
+    if (!g.init(cx, args, true))
         return false;
 
     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, args.length()))
         return BuildFlatMatchArray(cx, str, *fm, &args);
 
     /* Return if there was an error in tryFlatMatch. */
-    if (cx->isExceptionPending())  /* from tryFlatMatch */
+    if (cx->isExceptionPending())
         return false;
 
-    const RegExpMatcher *matcher = g.normalizeRegExp(false, 1, args);
-    if (!matcher)
+    if (!g.normalizeRegExp(cx, false, 1, args))
         return false;
 
     JSObject *array = NULL;
     MatchArgType arg = &array;
     RegExpStatics *res = cx->regExpStatics();
     Value rval;
-    if (!DoMatch(cx, res, str, *matcher, MatchCallback, arg, MATCH_ARGS, &rval))
+    if (!DoMatch(cx, res, str, g.regExp(), MatchCallback, arg, MATCH_ARGS, &rval))
         return false;
 
-    if (matcher->global())
+    if (g.regExp().global())
         args.rval() = ObjectOrNullValue(array);
     else
         args.rval() = rval;
     return true;
 }
 
 JSBool
 js::str_search(JSContext *cx, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JSString *str = ThisToStringForStringProto(cx, args);
     if (!str)
         return false;
 
-    RegExpGuard g(cx);
-    if (!g.init(args, true))
+    RegExpGuard g;
+    if (!g.init(cx, args, true))
         return false;
     if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, args.length())) {
         args.rval() = Int32Value(fm->match());
         return true;
     }
 
     if (cx->isExceptionPending())  /* from tryFlatMatch */
         return false;
 
-    const RegExpMatcher *matcher = g.normalizeRegExp(false, 1, args);
-    if (!matcher)
+    if (!g.normalizeRegExp(cx, false, 1, args))
         return false;
 
     JSLinearString *linearStr = str->ensureLinear(cx);
     if (!linearStr)
         return false;
 
     const jschar *chars = linearStr->chars();
     size_t length = linearStr->length();
     RegExpStatics *res = cx->regExpStatics();
 
     /* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */
     size_t i = 0;
     Value result;
-    if (!ExecuteRegExp(cx, res, *matcher, linearStr, chars, length, &i, RegExpTest, &result))
+    if (!ExecuteRegExp(cx, res, g.regExp(), linearStr, chars, length, &i, RegExpTest, &result))
         return false;
 
     if (result.isTrue())
         args.rval() = Int32Value(res->matchStart());
     else
         args.rval() = Int32Value(-1);
     return true;
 }
 
 struct ReplaceData
 {
     ReplaceData(JSContext *cx)
-     : g(cx), sb(cx)
+     : sb(cx)
     {}
 
     JSString           *str;           /* 'this' parameter object as a string */
     RegExpGuard        g;              /* regexp parameter object and private data */
     JSObject           *lambda;        /* replacement function object or null */
     JSObject           *elembase;      /* object for function(a){return b[a]} replace */
     JSLinearString     *repstr;        /* replacement string */
     const jschar       *dollar;        /* null or pointer to first $ in repstr */
@@ -2112,26 +2135,27 @@ BuildDollarReplacement(JSContext *cx, JS
 
     args->rval() = StringValue(builder.result());
     return true;
 }
 
 static inline bool
 str_replace_regexp(JSContext *cx, CallArgs args, ReplaceData &rdata)
 {
-    const RegExpMatcher *matcher = rdata.g.normalizeRegExp(true, 2, args);
-    if (!matcher)
+    if (!rdata.g.normalizeRegExp(cx, true, 2, args))
         return false;
 
     rdata.leftIndex = 0;
     rdata.calledBack = false;
 
     RegExpStatics *res = cx->regExpStatics();
+    RegExpShared &re = rdata.g.regExp();
+
     Value tmp;
-    if (!DoMatch(cx, res, rdata.str, *matcher, ReplaceRegExpCallback, &rdata, REPLACE_ARGS, &tmp))
+    if (!DoMatch(cx, res, rdata.str, re, ReplaceRegExpCallback, &rdata, REPLACE_ARGS, &tmp))
         return false;
 
     if (!rdata.calledBack) {
         /* Didn't match, so the string is unmodified. */
         args.rval() = StringValue(rdata.str);
         return true;
     }
 
@@ -2206,17 +2230,17 @@ js::str_replace(JSContext *cx, uintN arg
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     ReplaceData rdata(cx);
     rdata.str = ThisToStringForStringProto(cx, args);
     if (!rdata.str)
         return false;
 
-    if (!rdata.g.init(args))
+    if (!rdata.g.init(cx, args))
         return false;
 
     /* Extract replacement string/function. */
     if (args.length() >= ReplaceOptArg && js_IsCallable(args[1])) {
         rdata.lambda = &args[1].toObject();
         rdata.elembase = NULL;
         rdata.repstr = NULL;
         rdata.dollar = rdata.dollarEnd = NULL;
@@ -2278,17 +2302,16 @@ js::str_replace(JSContext *cx, uintN arg
      * this is observable behavior through the side-effect mutation of the
      * |RegExp| statics.
      */
 
     const FlatMatch *fm = rdata.g.tryFlatMatch(cx, rdata.str, ReplaceOptArg, args.length(), false);
     if (!fm) {
         if (cx->isExceptionPending())  /* oom in RopeMatch in tryFlatMatch */
             return false;
-        JS_ASSERT_IF(!rdata.g.matcherInitialized(), args.length() > ReplaceOptArg);
         return str_replace_regexp(cx, args, rdata);
     }
 
     if (fm->match() < 0) {
         args.rval() = StringValue(rdata.str);
         return true;
     }
 
@@ -2454,61 +2477,65 @@ SplitHelper(JSContext *cx, JSLinearStrin
         return NULL;
 
     /* Step 16. */
     return NewDenseCopiedArray(cx, splits.length(), splits.begin());
 }
 
 /*
  * The SplitMatch operation from ES5 15.5.4.14 is implemented using different
- * matchers for regular expression and string separators.
+ * paths for regular expression and string separators.
  *
- * The algorithm differs from the spec in that the matchers return the next
- * index at which a match happens.
+ * The algorithm differs from the spec in that the we return the next index at
+ * which a match happens.
  */
-class SplitRegExpMatcher {
+class SplitRegExpMatcher
+{
+    RegExpShared &re;
     RegExpStatics *res;
-    RegExpMatcher &matcher;
 
   public:
+    SplitRegExpMatcher(RegExpShared &re, RegExpStatics *res) : re(re), res(res) {}
+
     static const bool returnsCaptures = true;
-    SplitRegExpMatcher(RegExpMatcher &matcher, RegExpStatics *res) : res(res), matcher(matcher) {}
-
-    inline bool operator()(JSContext *cx, JSLinearString *str, size_t index,
-                           SplitMatchResult *result) {
+
+    bool operator()(JSContext *cx, JSLinearString *str, size_t index, SplitMatchResult *result)
+    {
         Value rval = UndefinedValue();
         const jschar *chars = str->chars();
         size_t length = str->length();
-        if (!ExecuteRegExp(cx, res, matcher, str, chars, length, &index, RegExpTest, &rval))
+        if (!ExecuteRegExp(cx, res, re, str, chars, length, &index, RegExpTest, &rval))
             return false;
         if (!rval.isTrue()) {
             result->setFailure();
             return true;
         }
         JSSubString sep;
         res->getLastMatch(&sep);
 
         result->setResult(sep.length, index);
         return true;
     }
 };
 
-class SplitStringMatcher {
+class SplitStringMatcher
+{
     const jschar *sepChars;
     size_t sepLength;
 
   public:
-    static const bool returnsCaptures = false;
     SplitStringMatcher(JSLinearString *sep) {
         sepChars = sep->chars();
         sepLength = sep->length();
     }
 
-    inline bool operator()(JSContext *cx, JSLinearString *str, size_t index,
-                           SplitMatchResult *res) {
+    static const bool returnsCaptures = false;
+
+    bool operator()(JSContext *cx, JSLinearString *str, size_t index, SplitMatchResult *res)
+    {
         JS_ASSERT(index == 0 || index < str->length());
         const jschar *chars = str->chars();
         jsint match = StringMatch(chars + index, str->length() - index, sepChars, sepLength);
         if (match == -1)
             res->setFailure();
         else
             res->setResult(sepLength, index + match + sepLength);
         return true;
@@ -2538,25 +2565,25 @@ js::str_split(JSContext *cx, uintN argc,
         if (!ToNumber(cx, args[1], &d))
             return false;
         limit = js_DoubleToECMAUint32(d);
     } else {
         limit = UINT32_MAX;
     }
 
     /* Step 8. */
-    RegExpMatcher matcher(cx);
+    RegExpShared::Guard re;
     JSLinearString *sepstr = NULL;
     bool sepUndefined = (args.length() == 0 || args[0].isUndefined());
     if (!sepUndefined) {
         if (IsObjectWithClass(args[0], ESClass_RegExp, cx)) {
             RegExpShared *shared = RegExpToShared(cx, args[0].toObject());
             if (!shared)
                 return false;
-            matcher.init(NeedsIncRef<RegExpShared>(shared));
+            re.init(*shared);
         } else {
             sepstr = ArgToRootedString(cx, args, 0);
             if (!sepstr)
                 return false;
         }
     }
 
     /* Step 9. */
@@ -2580,23 +2607,20 @@ js::str_split(JSContext *cx, uintN argc,
         return true;
     }
     JSLinearString *strlin = str->ensureLinear(cx);
     if (!strlin)
         return false;
 
     /* Steps 11-15. */
     JSObject *aobj;
-    if (!matcher.initialized()) {
-        // NB: sepstr is anchored through its storage in args[0].
+    if (!re.initialized())
         aobj = SplitHelper(cx, strlin, limit, SplitStringMatcher(sepstr), type);
-    } else {
-        aobj = SplitHelper(cx, strlin, limit,
-                           SplitRegExpMatcher(matcher, cx->regExpStatics()), type);
-    }
+    else
+        aobj = SplitHelper(cx, strlin, limit, SplitRegExpMatcher(*re, cx->regExpStatics()), type);
     if (!aobj)
         return false;
 
     /* Step 16. */
     aobj->setType(type);
     args.rval() = ObjectValue(*aobj);
     return true;
 }
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -188,106 +188,16 @@ class AutoScopedAssign
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
         *addr = value;
     }
 
     ~AutoScopedAssign() { *addr = old; }
 };
 
-template <class RefCountable>
-class AlreadyIncRefed
-{
-    typedef RefCountable *****ConvertibleToBool;
-
-    RefCountable *obj;
-
-  public:
-    explicit AlreadyIncRefed(RefCountable *obj = NULL) : obj(obj) {}
-
-    bool null() const { return obj == NULL; }
-    operator ConvertibleToBool() const { return (ConvertibleToBool)obj; }
-
-    RefCountable *operator->() const { JS_ASSERT(!null()); return obj; }
-    RefCountable &operator*() const { JS_ASSERT(!null()); return *obj; }
-    RefCountable *get() const { return obj; }
-};
-
-template <class RefCountable>
-class NeedsIncRef
-{
-    typedef RefCountable *****ConvertibleToBool;
-
-    RefCountable *obj;
-
-  public:
-    explicit NeedsIncRef(RefCountable *obj = NULL) : obj(obj) {}
-
-    bool null() const { return obj == NULL; }
-    operator ConvertibleToBool() const { return (ConvertibleToBool)obj; }
-
-    RefCountable *operator->() const { JS_ASSERT(!null()); return obj; }
-    RefCountable &operator*() const { JS_ASSERT(!null()); return *obj; }
-    RefCountable *get() const { return obj; }
-};
-
-template <class RefCountable>
-class AutoRefCount
-{
-    typedef RefCountable *****ConvertibleToBool;
-
-    JSContext *const cx;
-    RefCountable *obj;
-
-    AutoRefCount(const AutoRefCount &other) MOZ_DELETE;
-    void operator=(const AutoRefCount &other) MOZ_DELETE;
-
-  public:
-    explicit AutoRefCount(JSContext *cx)
-      : cx(cx), obj(NULL)
-    {}
-
-    AutoRefCount(JSContext *cx, NeedsIncRef<RefCountable> aobj)
-      : cx(cx), obj(aobj.get())
-    {
-        if (obj)
-            obj->incref(cx);
-    }
-
-    AutoRefCount(JSContext *cx, AlreadyIncRefed<RefCountable> aobj)
-      : cx(cx), obj(aobj.get())
-    {}
-
-    ~AutoRefCount() {
-        if (obj)
-            obj->decref(cx);
-    }
-
-    void reset(NeedsIncRef<RefCountable> aobj) {
-        if (obj)
-            obj->decref(cx);
-        obj = aobj.get();
-        if (obj)
-            obj->incref(cx);
-    }
-
-    void reset(AlreadyIncRefed<RefCountable> aobj) {
-        if (obj)
-            obj->decref(cx);
-        obj = aobj.get();
-    }
-
-    bool null() const { return obj == NULL; }
-    operator ConvertibleToBool() const { return (ConvertibleToBool)obj; }
-
-    RefCountable *operator->() const { JS_ASSERT(!null()); return obj; }
-    RefCountable &operator*() const { JS_ASSERT(!null()); return *obj; }
-    RefCountable *get() const { return obj; }
-};
-
 template <class T>
 JS_ALWAYS_INLINE static void
 PodZero(T *t)
 {
     memset(t, 0, sizeof(T));
 }
 
 template <class T>
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -221,17 +221,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number is XDR'd near the front of xdr bytecode and
  * aborts deserialization if there is a mismatch between the current
  * and saved versions. If deserialization fails, the data should be
  * invalidated if possible.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 106)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 107)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 JS_END_EXTERN_C
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -405,17 +405,17 @@ mjit::Compiler::scanInlineCalls(uint32_t
 }
 
 CompileStatus
 mjit::Compiler::pushActiveFrame(JSScript *script, uint32_t argc)
 {
     if (cx->runtime->profilingScripts && !script->pcCounters)
         script->initCounts(cx);
 
-    ActiveFrame *newa = cx->new_<ActiveFrame>(cx);
+    ActiveFrame *newa = OffTheBooks::new_<ActiveFrame>(cx);
     if (!newa)
         return Compile_Error;
 
     newa->parent = a;
     if (a)
         newa->parentPC = PC;
     newa->script = script;
     newa->mainCodeStart = masm.size();
@@ -454,17 +454,17 @@ mjit::Compiler::pushActiveFrame(JSScript
     }
 #endif
 
     if (!frame.pushActiveFrame(script, argc)) {
         js_ReportOutOfMemory(cx);
         return Compile_Error;
     }
 
-    newa->jumpMap = (Label *)cx->malloc_(sizeof(Label) * script->length);
+    newa->jumpMap = (Label *)OffTheBooks::malloc_(sizeof(Label) * script->length);
     if (!newa->jumpMap) {
         js_ReportOutOfMemory(cx);
         return Compile_Error;
     }
 #ifdef DEBUG
     for (uint32_t i = 0; i < script->length; i++)
         newa->jumpMap[i] = Label();
 #endif
@@ -613,17 +613,17 @@ mjit::Compiler::prepareInferenceTypes(JS
      *
      * When we get to a branch and need to know a variable's value at the
      * branch target, we know it will either be a phi node at the target or
      * the variable's current value, as no phi node is created at the target
      * only if a variable has the same value on all incoming edges.
      */
 
     a->varTypes = (VarType *)
-        cx->calloc_(TotalSlots(script) * sizeof(VarType));
+        OffTheBooks::calloc_(TotalSlots(script) * sizeof(VarType));
     if (!a->varTypes)
         return Compile_Error;
 
     for (uint32_t slot = ArgSlot(0); slot < TotalSlots(script); slot++) {
         VarType &vt = a->varTypes[slot];
         vt.setTypes(types::TypeScript::SlotTypes(script, slot));
     }
 
@@ -866,17 +866,17 @@ MakeJITScript(JSContext *cx, JSScript *s
                 return NULL;
         }
     }
 #endif /* !JS_CPU_X64 */
 
     size_t dataSize = sizeof(JITScript)
         + (chunks.length() * sizeof(ChunkDescriptor))
         + (edges.length() * sizeof(CrossChunkEdge));
-    uint8_t *cursor = (uint8_t *) cx->calloc_(dataSize);
+    uint8_t *cursor = (uint8_t *) OffTheBooks::calloc_(dataSize);
     if (!cursor)
         return NULL;
 
     JITScript *jit = (JITScript *) cursor;
     cursor += sizeof(JITScript);
 
     jit->script = script;
     JS_INIT_CLIST(&jit->callers);
@@ -1199,17 +1199,17 @@ mjit::Compiler::generatePrologue()
         prepareStubCall(Uses(0));
         INLINE_STUBCALL(stubs::ScriptProbeOnlyPrologue, REJOIN_RESUME);
     }
 
     recompileCheckHelper();
 
     if (outerScript->pcCounters || Probes::wantNativeAddressInfo(cx)) {
         size_t length = ssa.frameLength(ssa.numFrames() - 1);
-        pcLengths = (PCLengthEntry *) cx->calloc_(sizeof(pcLengths[0]) * length);
+        pcLengths = (PCLengthEntry *) OffTheBooks::calloc_(sizeof(pcLengths[0]) * length);
         if (!pcLengths)
             return Compile_Error;
     }
 
     return Compile_Okay;
 }
 
 void
@@ -1347,17 +1347,17 @@ mjit::Compiler::finishThisUp()
 #endif
 #if defined JS_POLYIC
                       sizeof(ic::PICInfo) * pics.length() +
                       sizeof(ic::GetElementIC) * getElemICs.length() +
                       sizeof(ic::SetElementIC) * setElemICs.length() +
 #endif
                       0;
 
-    uint8_t *cursor = (uint8_t *)cx->calloc_(dataSize);
+    uint8_t *cursor = (uint8_t *)OffTheBooks::calloc_(dataSize);
     if (!cursor) {
         execPool->release();
         js_ReportOutOfMemory(cx);
         return Compile_Error;
     }
 
     JITChunk *chunk = new(cursor) JITChunk;
     cursor += sizeof(JITChunk);
@@ -1799,17 +1799,17 @@ mjit::Compiler::finishThisUp()
                 if (edge.jumpTableEntries)
                     cx->free_(edge.jumpTableEntries);
                 CrossChunkEdge::JumpTableEntryVector *jumpTableEntries = NULL;
                 bool failed = false;
                 for (unsigned j = 0; j < chunkJumps.length(); j++) {
                     ChunkJumpTableEdge nedge = chunkJumps[j];
                     if (nedge.edge.source == edge.source && nedge.edge.target == edge.target) {
                         if (!jumpTableEntries) {
-                            jumpTableEntries = cx->new_<CrossChunkEdge::JumpTableEntryVector>();
+                            jumpTableEntries = OffTheBooks::new_<CrossChunkEdge::JumpTableEntryVector>();
                             if (!jumpTableEntries)
                                 failed = true;
                         }
                         if (!jumpTableEntries->append(nedge.jumpTableEntry))
                             failed = true;
                         *nedge.jumpTableEntry = label;
                     }
                 }
@@ -5625,26 +5625,28 @@ mjit::Compiler::jsop_setprop(PropertyNam
                 bumpPropCounter(PC, OpcodeCounts::PROP_DEFINITE);
             return true;
         }
     }
 
     if (script->pcCounters)
         bumpPropCounter(PC, OpcodeCounts::PROP_OTHER);
 
+    JSOp op = JSOp(*PC);
+
 #ifdef JSGC_INCREMENTAL_MJ
-    /* Write barrier. */
-    if (cx->compartment->needsBarrier() && (!types || types->propertyNeedsBarrier(cx, id))) {
+    /* Write barrier. We don't have type information for JSOP_SETNAME. */
+    if (cx->compartment->needsBarrier() &&
+        (!types || op == JSOP_SETNAME || types->propertyNeedsBarrier(cx, id)))
+    {
         jsop_setprop_slow(name);
         return true;
     }
 #endif
 
-    JSOp op = JSOp(*PC);
-
     ic::PICInfo::Kind kind = (op == JSOP_SETMETHOD)
                              ? ic::PICInfo::SETMETHOD
                              : ic::PICInfo::SET;
     PICGenInfo pic(kind, op);
     pic.name = name;
 
     if (monitored(PC)) {
         pic.typeMonitored = true;
@@ -6937,37 +6939,33 @@ mjit::Compiler::jsop_regexp()
                     frame.push(ObjectValue(*obj));
                     return true;
                 }
             }
         }
     }
 
     /*
-     * Force creation of the RegExpPrivate in the script's RegExpObject
+     * Force creation of the RegExpShared in the script's RegExpObject
      * so that we grab it in the getNewObject template copy. Note that
      * JIT code is discarded on every GC, which permits us to burn in
-     * the pointer to the RegExpPrivate refcount.
+     * the pointer to the RegExpShared.
      */
     if (!reobj->getShared(cx))
         return false;
 
     RegisterID result = frame.allocReg();
     Jump emptyFreeList = masm.getNewObject(cx, result, obj);
 
     stubcc.linkExit(emptyFreeList, Uses(0));
     stubcc.leave();
 
     stubcc.masm.move(ImmPtr(obj), Registers::ArgReg1);
     OOL_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH);
 
-    /* Bump the refcount on the wrapped RegExp. */
-    size_t *refcount = reobj->addressOfPrivateRefCount();
-    masm.add32(Imm32(1), AbsoluteAddress(refcount));
-
     frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result);
 
     stubcc.rejoin(Changes(1));
     return true;
 }
 
 bool
 mjit::Compiler::startLoop(jsbytecode *head, Jump entry, jsbytecode *entryTarget)
@@ -6980,17 +6978,17 @@ mjit::Compiler::startLoop(jsbytecode *he
          * Convert all loop registers in the outer loop into unassigned registers.
          * We don't keep track of which registers the inner loop uses, so the only
          * registers that can be carried in the outer loop must be mentioned before
          * the inner loop starts.
          */
         loop->clearLoopRegisters();
     }
 
-    LoopState *nloop = cx->new_<LoopState>(cx, &ssa, this, &frame);
+    LoopState *nloop = OffTheBooks::new_<LoopState>(cx, &ssa, this, &frame);
     if (!nloop || !nloop->init(head, entry, entryTarget))
         return false;
 
     nloop->outer = loop;
     loop = nloop;
     frame.setLoop(loop);
 
     return true;
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -89,17 +89,17 @@ bool
 FrameState::pushActiveFrame(JSScript *script, uint32_t argc)
 {
     if (!a) {
         this->nentries = analyze::TotalSlots(script) + (script->nslots - script->nfixed) +
             StackSpace::STACK_JIT_EXTRA - VALUES_PER_STACK_FRAME;
         size_t totalBytes = sizeof(FrameEntry) * nentries +       // entries[]
                             sizeof(FrameEntry *) * nentries +     // tracker.entries
                             sizeof(StackEntryExtra) * nentries;   // extraArray
-        uint8_t *cursor = (uint8_t *)cx->calloc_(totalBytes);
+        uint8_t *cursor = (uint8_t *)OffTheBooks::calloc_(totalBytes);
         if (!cursor)
             return false;
 
         this->entries = (FrameEntry *) cursor;
         cursor += sizeof(FrameEntry) * nentries;
 
         this->tracker.entries = (FrameEntry **)cursor;
         cursor += sizeof(FrameEntry *) * nentries;
@@ -115,17 +115,17 @@ FrameState::pushActiveFrame(JSScript *sc
 #endif
 
         this->temporaries = this->temporariesTop = this->entries + nentries - TEMPORARY_LIMIT;
     }
 
     /* We should have already checked that argc == nargs */
     JS_ASSERT_IF(a, argc == script->function()->nargs);
 
-    ActiveFrame *newa = cx->new_<ActiveFrame>();
+    ActiveFrame *newa = OffTheBooks::new_<ActiveFrame>();
     if (!newa)
         return false;
 
     newa->parent = a;
     newa->depth = a ? (totalDepth() + VALUES_PER_STACK_FRAME) : 0;
 
     newa->script = script;
     newa->PC = script->code;
@@ -2880,17 +2880,17 @@ FrameState::getTemporaryCopies(Uses uses
     for (FrameEntry *fe = temporaries; fe < temporariesTop; fe++) {
         if (!fe->isTracked())
             continue;
         if (fe->isCopied()) {
             for (uint32_t i = fe->trackerIndex() + 1; i < tracker.nentries; i++) {
                 FrameEntry *nfe = tracker[i];
                 if (!deadEntry(nfe, uses.nuses) && nfe->isCopy() && nfe->copyOf() == fe) {
                     if (!res)
-                        res = cx->new_< Vector<TemporaryCopy> >(cx);
+                        res = OffTheBooks::new_< Vector<TemporaryCopy> >(cx);
                     res->append(TemporaryCopy(addressOf(nfe), addressOf(fe)));
                 }
             }
         }
     }
 
     return res;
 }
--- a/js/src/methodjit/ImmutableSync.cpp
+++ b/js/src/methodjit/ImmutableSync.cpp
@@ -59,17 +59,17 @@ ImmutableSync::~ImmutableSync()
 }
 
 bool
 ImmutableSync::init(JSContext *cx, const FrameState &frame, uint32_t nentries)
 {
     this->cx = cx;
     this->frame = &frame;
 
-    entries = (SyncEntry *)cx->calloc_(sizeof(SyncEntry) * nentries);
+    entries = (SyncEntry *)OffTheBooks::calloc_(sizeof(SyncEntry) * nentries);
     return !!entries;
 }
 
 void
 ImmutableSync::reset(Assembler *masm, Registers avail, FrameEntry *top, FrameEntry *bottom)
 {
     this->avail = avail;
     this->masm = masm;
--- a/js/src/methodjit/PolyIC.h
+++ b/js/src/methodjit/PolyIC.h
@@ -192,17 +192,17 @@ class BasePolyIC : public BaseIC {
     bool addPool(JSContext *cx, JSC::ExecutablePool *pool) {
         if (areZeroPools()) {
             u.execPool = pool;
             return true;
         }
         if (isOnePool()) {
             JSC::ExecutablePool *oldPool = u.execPool;
             JS_ASSERT(!isTagged(oldPool));
-            ExecPoolVector *execPools = cx->new_<ExecPoolVector>(SystemAllocPolicy()); 
+            ExecPoolVector *execPools = OffTheBooks::new_<ExecPoolVector>(SystemAllocPolicy()); 
             if (!execPools)
                 return false;
             if (!execPools->append(oldPool) || !execPools->append(pool)) {
                 Foreground::delete_(execPools);
                 return false;
             }
             u.taggedExecPools = tag(execPools);
             return true;
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1099,17 +1099,17 @@ stubs::RegExp(VMFrame &f, JSObject *rege
     /*
      * Push a regexp object cloned from the regexp literal object mapped by the
      * bytecode at pc.
      */
     JSObject *proto = f.fp()->scopeChain().global().getOrCreateRegExpPrototype(f.cx);
     if (!proto)
         THROW();
     JS_ASSERT(proto);
-    JSObject *obj = js_CloneRegExpObject(f.cx, regex, proto);
+    JSObject *obj = CloneRegExpObject(f.cx, regex, proto);
     if (!obj)
         THROW();
     f.regs.sp[0].setObject(*obj);
 }
 
 JSObject * JS_FASTCALL
 stubs::LambdaJoinableForInit(VMFrame &f, JSFunction *fun)
 {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3881,16 +3881,20 @@ MJitChunkLimit(JSContext *cx, uintN argc
     jsdouble t;
     if (!JS_ValueToNumber(cx, JS_ARGV(cx, vp)[0], &t))
         return JS_FALSE;
 
 #ifdef JS_METHODJIT
     mjit::SetChunkLimit((uint32_t) t);
 #endif
 
+    // Clear out analysis information which might refer to code compiled with
+    // the previous chunk limit.
+    JS_GC(cx);
+
     vp->setUndefined();
     return true;
 }
 
 enum CompartmentKind { SAME_COMPARTMENT, NEW_COMPARTMENT };
 
 static JSObject *
 NewGlobalObject(JSContext *cx, CompartmentKind compartment);
--- a/js/src/vm/RegExpObject-inl.h
+++ b/js/src/vm/RegExpObject-inl.h
@@ -54,160 +54,35 @@ inline js::RegExpObject &
 JSObject::asRegExp()
 {
     JS_ASSERT(isRegExp());
     return *static_cast<js::RegExpObject *>(this);
 }
 
 namespace js {
 
-inline bool
-IsRegExpMetaChar(jschar c)
-{
-    switch (c) {
-      /* Taken from the PatternCharacter production in 15.10.1. */
-      case '^': case '$': case '\\': case '.': case '*': case '+':
-      case '?': case '(': case ')': case '[': case ']': case '{':
-      case '}': case '|':
-        return true;
-      default:
-        return false;
-    }
-}
-
-inline bool
-HasRegExpMetaChars(const jschar *chars, size_t length)
-{
-    for (size_t i = 0; i < length; ++i) {
-        if (IsRegExpMetaChar(chars[i]))
-            return true;
-    }
-    return false;
-}
-
-inline bool
-RegExpObject::startsWithAtomizedGreedyStar() const
+inline RegExpShared &
+RegExpObject::shared() const
 {
-    JSLinearString *source = getSource();
-
-    if (!source->isAtom())
-        return false;
-
-    if (source->length() < 3)
-        return false;
-
-    const jschar *chars = source->chars();
-    return chars[0] == detail::GreedyStarChars[0] &&
-           chars[1] == detail::GreedyStarChars[1] &&
-           chars[2] != '?';
-}
-
-inline size_t *
-RegExpObject::addressOfPrivateRefCount() const
-{
-    return shared().addressOfRefCount();
-}
-
-inline RegExpObject *
-RegExpObject::create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
-                     RegExpFlag flags, TokenStream *tokenStream)
-{
-    RegExpFlag staticsFlags = res->getFlags();
-    return createNoStatics(cx, chars, length, RegExpFlag(flags | staticsFlags), tokenStream);
-}
-
-inline RegExpObject *
-RegExpObject::createNoStatics(JSContext *cx, const jschar *chars, size_t length,
-                              RegExpFlag flags, TokenStream *tokenStream)
-{
-    JSAtom *source = js_AtomizeChars(cx, chars, length);
-    if (!source)
-        return NULL;
-
-    return createNoStatics(cx, source, flags, tokenStream);
+    JS_ASSERT(JSObject::getPrivate() != NULL);
+    return *static_cast<RegExpShared *>(JSObject::getPrivate());
 }
 
-inline RegExpObject *
-RegExpObject::createNoStatics(JSContext *cx, JSAtom *source, RegExpFlag flags,
-                              TokenStream *tokenStream)
-{
-    if (!RegExpCode::checkSyntax(cx, tokenStream, source))
-        return NULL;
-
-    RegExpObjectBuilder builder(cx);
-    return builder.build(source, flags);
-}
-
-inline void
-RegExpObject::purge(JSContext *cx)
+inline RegExpShared *
+RegExpObject::maybeShared()
 {
-    if (RegExpShared *shared = maybeShared()) {
-        shared->decref(cx);
-        JSObject::setPrivate(NULL);
-    }
-}
-
-inline void
-RegExpObject::finalize(JSContext *cx)
-{
-    purge(cx);
-#ifdef DEBUG
-    JSObject::setPrivate((void *) 0x1); /* Non-null but still in the zero page. */
-#endif
+    return static_cast<RegExpShared *>(JSObject::getPrivate());
 }
 
-inline bool
-RegExpObject::init(JSContext *cx, JSLinearString *source, RegExpFlag flags)
+inline RegExpShared *
+RegExpObject::getShared(JSContext *cx)
 {
-    if (nativeEmpty()) {
-        if (isDelegate()) {
-            if (!assignInitialShape(cx))
-                return false;
-        } else {
-            Shape *shape = assignInitialShape(cx);
-            if (!shape)
-                return false;
-            EmptyShape::insertInitialShape(cx, shape, getProto());
-        }
-        JS_ASSERT(!nativeEmpty());
-    }
-
-    DebugOnly<JSAtomState *> atomState = &cx->runtime->atomState;
-    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->lastIndexAtom))->slot() == LAST_INDEX_SLOT);
-    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->sourceAtom))->slot() == SOURCE_SLOT);
-    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->globalAtom))->slot() == GLOBAL_FLAG_SLOT);
-    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->ignoreCaseAtom))->slot() ==
-                                 IGNORE_CASE_FLAG_SLOT);
-    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->multilineAtom))->slot() ==
-                                 MULTILINE_FLAG_SLOT);
-    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->stickyAtom))->slot() == STICKY_FLAG_SLOT);
-
-    JS_ASSERT(!maybeShared());
-    zeroLastIndex();
-    setSource(source);
-    setGlobal(flags & GlobalFlag);
-    setIgnoreCase(flags & IgnoreCaseFlag);
-    setMultiline(flags & MultilineFlag);
-    setSticky(flags & StickyFlag);
-    return true;
-}
-
-inline void
-RegExpMatcher::init(NeedsIncRef<RegExpShared> shared)
-{
-    JS_ASSERT(!shared_);
-    shared_.reset(shared);
-}
-
-inline bool
-RegExpMatcher::init(JSLinearString *patstr, JSString *opt)
-{
-    JS_ASSERT(!shared_);
-    shared_.reset(RegExpShared::create(cx_, patstr, opt, NULL));
-    return !!shared_;
+    if (RegExpShared *shared = maybeShared())
+        return shared;
+    return createShared(cx);
 }
 
 inline void
 RegExpObject::setLastIndex(const Value &v)
 {
     setSlot(LAST_INDEX_SLOT, v);
 }
 
@@ -219,17 +94,17 @@ RegExpObject::setLastIndex(double d)
 
 inline void
 RegExpObject::zeroLastIndex()
 {
     setSlot(LAST_INDEX_SLOT, Int32Value(0));
 }
 
 inline void
-RegExpObject::setSource(JSLinearString *source)
+RegExpObject::setSource(JSAtom *source)
 {
     setSlot(SOURCE_SLOT, StringValue(source));
 }
 
 inline void
 RegExpObject::setIgnoreCase(bool enabled)
 {
     setSlot(IGNORE_CASE_FLAG_SLOT, BooleanValue(enabled));
@@ -248,263 +123,27 @@ RegExpObject::setMultiline(bool enabled)
 }
 
 inline void
 RegExpObject::setSticky(bool enabled)
 {
     setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled));
 }
 
-/* RegExpShared inlines. */
-
-inline bool
-RegExpShared::cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
-                          RegExpCacheKind targetKind,
-                          AlreadyIncRefed<RegExpShared> *result)
-{
-    RegExpCache *cache = cx->runtime->getRegExpCache(cx);
-    if (!cache)
-        return false;
-
-    if (RegExpCache::Ptr p = cache->lookup(atom)) {
-        RegExpCacheValue &cacheValue = p->value;
-        if (cacheValue.kind() == targetKind && cacheValue.shared().getFlags() == flags) {
-            cacheValue.shared().incref(cx);
-            *result = AlreadyIncRefed<RegExpShared>(&cacheValue.shared());
-            return true;
-        }
-    }
-
-    JS_ASSERT(result->null());
-    return true;
-}
-
-inline bool
-RegExpShared::cacheInsert(JSContext *cx, JSAtom *atom, RegExpCacheKind kind,
-                          RegExpShared &shared)
-{
-    /*
-     * Note: allocation performed since lookup may cause a garbage collection,
-     * so we have to re-lookup the cache (and inside the cache) after the
-     * allocation is performed.
-     */
-    RegExpCache *cache = cx->runtime->getRegExpCache(cx);
-    if (!cache)
-        return false;
-
-    if (RegExpCache::AddPtr addPtr = cache->lookupForAdd(atom)) {
-        /* We clobber existing entries with the same source (but different flags or kind). */
-        JS_ASSERT(addPtr->value.shared().getFlags() != shared.getFlags() ||
-                  addPtr->value.kind() != kind);
-        addPtr->value.reset(shared, kind);
-    } else {
-        if (!cache->add(addPtr, atom, RegExpCacheValue(shared, kind))) {
-            js_ReportOutOfMemory(cx);
-            return false;
-        }
-    }
-
-    return true;
-}
-
-inline AlreadyIncRefed<RegExpShared>
-RegExpShared::create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts)
-{
-    typedef AlreadyIncRefed<RegExpShared> RetType;
-
-    /*
-     * We choose to only cache |RegExpShared|s who have atoms as
-     * sources, under the unverified premise that non-atoms will have a
-     * low hit rate (both hit ratio and absolute number of hits).
-     */
-    bool cacheable = source->isAtom();
-    if (!cacheable)
-        return RetType(RegExpShared::createUncached(cx, source, flags, ts));
-
-    /*
-     * Refcount note: not all |RegExpShared|s are cached so we need to keep a
-     * refcount. The cache holds a "weak ref", where the |RegExpShared|'s
-     * deallocation decref will first cause it to remove itself from the cache.
-     */
-
-    JSAtom *sourceAtom = &source->asAtom();
-
-    AlreadyIncRefed<RegExpShared> cached;
-    if (!cacheLookup(cx, sourceAtom, flags, detail::RegExpCache_ExecCapable, &cached))
-        return RetType(NULL);
-
-    if (cached)
-        return cached;
-
-    RegExpShared *shared = RegExpShared::createUncached(cx, source, flags, ts);
-    if (!shared)
-        return RetType(NULL);
-
-    if (!cacheInsert(cx, sourceAtom, detail::RegExpCache_ExecCapable, *shared))
-        return RetType(NULL);
-
-    return RetType(shared);
-}
-
 /* This function should be deleted once bad Android platforms phase out. See bug 604774. */
 inline bool
 detail::RegExpCode::isJITRuntimeEnabled(JSContext *cx)
 {
 #if defined(ANDROID) && defined(JS_METHODJIT)
     return cx->methodJitEnabled;
 #else
     return true;
 #endif
 }
 
-inline bool
-detail::RegExpCode::compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts,
-                            uintN *parenCount, RegExpFlag flags)
-{
-#if ENABLE_YARR_JIT
-    /* Parse the pattern. */
-    ErrorCode yarrError;
-    YarrPattern yarrPattern(pattern, bool(flags & IgnoreCaseFlag), bool(flags & MultilineFlag),
-                            &yarrError);
-    if (yarrError) {
-        reportYarrError(cx, ts, yarrError);
-        return false;
-    }
-    *parenCount = yarrPattern.m_numSubpatterns;
-
-    /*
-     * The YARR JIT compiler attempts to compile the parsed pattern. If
-     * it cannot, it informs us via |codeBlock.isFallBack()|, in which
-     * case we have to bytecode compile it.
-     */
-
-#ifdef JS_METHODJIT
-    if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) {
-        JSC::ExecutableAllocator *execAlloc = cx->runtime->getExecutableAllocator(cx);
-        if (!execAlloc) {
-            js_ReportOutOfMemory(cx);
-            return false;
-        }
-
-        JSGlobalData globalData(execAlloc);
-        jitCompile(yarrPattern, &globalData, codeBlock);
-        if (!codeBlock.isFallBack())
-            return true;
-    }
-#endif
-
-    WTF::BumpPointerAllocator *bumpAlloc = cx->runtime->getBumpPointerAllocator(cx);
-    if (!bumpAlloc) {
-        js_ReportOutOfMemory(cx);
-        return false;
-    }
-
-    codeBlock.setFallBack(true);
-    byteCode = byteCompile(yarrPattern, bumpAlloc).get();
-    return true;
-#else /* !defined(ENABLE_YARR_JIT) */
-    int error = 0;
-    compiled = jsRegExpCompile(pattern.chars(), pattern.length(),
-                  ignoreCase() ? JSRegExpIgnoreCase : JSRegExpDoNotIgnoreCase,
-                  multiline() ? JSRegExpMultiline : JSRegExpSingleLine,
-                  parenCount, &error);
-    if (error) {
-        reportPCREError(cx, error);
-        return false;
-    }
-    return true;
-#endif
-}
-
-inline bool
-RegExpShared::compile(JSContext *cx, TokenStream *ts)
-{
-    if (!sticky())
-        return code.compile(cx, *source, ts, &parenCount, getFlags());
-
-    /*
-     * The sticky case we implement hackily by prepending a caret onto the front
-     * and relying on |::execute| to pseudo-slice the string when it sees a sticky regexp.
-     */
-    static const jschar prefix[] = {'^', '(', '?', ':'};
-    static const jschar postfix[] = {')'};
-
-    using mozilla::ArrayLength;
-    StringBuffer sb(cx);
-    if (!sb.reserve(ArrayLength(prefix) + source->length() + ArrayLength(postfix)))
-        return false;
-    sb.infallibleAppend(prefix, ArrayLength(prefix));
-    sb.infallibleAppend(source->chars(), source->length());
-    sb.infallibleAppend(postfix, ArrayLength(postfix));
-
-    JSLinearString *fakeySource = sb.finishString();
-    if (!fakeySource)
-        return false;
-    return code.compile(cx, *fakeySource, ts, &parenCount, getFlags());
-}
-
-inline RegExpRunStatus
-detail::RegExpCode::execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
-                            int *output, size_t outputCount)
-{
-    int result;
-#if ENABLE_YARR_JIT
-    (void) cx; /* Unused. */
-    if (codeBlock.isFallBack())
-        result = JSC::Yarr::interpret(byteCode, chars, start, length, output);
-    else
-        result = JSC::Yarr::execute(codeBlock, chars, start, length, output);
-#else
-    result = jsRegExpExecute(cx, compiled, chars, length, start, output, outputCount);
-#endif
-
-    if (result == -1)
-        return RegExpRunStatus_Success_NotFound;
-
-#if !ENABLE_YARR_JIT
-    if (result < 0) {
-        reportPCREError(cx, result);
-        return RegExpRunStatus_Error;
-    }
-#endif
-
-    JS_ASSERT(result >= 0);
-    return RegExpRunStatus_Success;
-}
-
-inline void
-RegExpShared::incref(JSContext *cx)
-{
-    ++refCount;
-}
-
-inline void
-RegExpShared::decref(JSContext *cx)
-{
-    if (--refCount != 0)
-        return;
-
-    if (RegExpCache *cache = cx->runtime->maybeRegExpCache()) {
-        if (source->isAtom()) {
-            if (RegExpCache::Ptr p = cache->lookup(&source->asAtom())) {
-                if (&p->value.shared() == this)
-                    cache->remove(p);
-            }
-        }
-    }
-
-#ifdef DEBUG
-    this->~RegExpShared();
-    memset(this, 0xcd, sizeof(*this));
-    cx->free_(this);
-#else
-    cx->delete_(this);
-#endif
-}
-
 inline RegExpShared *
 RegExpToShared(JSContext *cx, JSObject &obj)
 {
     JS_ASSERT(ObjectClassIs(obj, ESClass_RegExp, cx));
     if (obj.isRegExp())
         return obj.asRegExp().getShared(cx);
     return Proxy::regexp_toShared(cx, &obj);
 }
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -51,40 +51,25 @@
 using namespace js;
 using js::detail::RegExpCode;
 
 JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD);
 JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
 JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE);
 JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
 
-/* RegExpMatcher */
-
-bool
-RegExpMatcher::initWithTestOptimized(RegExpObject &reobj)
-{
-    JS_ASSERT(reobj.startsWithAtomizedGreedyStar());
-    JS_ASSERT(!shared_);
+/* RegExpObjectBuilder */
 
-    JSAtom *source = &reobj.getSource()->asAtom();
-    shared_.reset(RegExpShared::createTestOptimized(cx_, source, reobj.getFlags()));
-    if (!shared_)
-        return false;
-
-    /*
-     * Create a dummy RegExpObject to persist this RegExpShared until the next GC.
-     * Note that we give the ref we have to this new object.
-     */
-    RegExpObjectBuilder builder(cx_);
-    shared_->incref(cx_);
-    return !!builder.build(AlreadyIncRefed<RegExpShared>(shared_.get()));
+RegExpObjectBuilder::RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj)
+  : cx(cx), reobj_(reobj)
+{
+    if (reobj_)
+        reobj_->setPrivate(NULL);
 }
 
-/* RegExpObjectBuilder */
-
 bool
 RegExpObjectBuilder::getOrCreate()
 {
     if (reobj_)
         return true;
 
     JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
     if (!obj)
@@ -105,40 +90,34 @@ RegExpObjectBuilder::getOrCreateClone(Re
         return false;
     clone->setPrivate(NULL);
 
     reobj_ = &clone->asRegExp();
     return true;
 }
 
 RegExpObject *
-RegExpObjectBuilder::build(AlreadyIncRefed<RegExpShared> shared)
+RegExpObjectBuilder::build(JSAtom *source, RegExpShared &shared)
 {
-    if (!getOrCreate()) {
-        shared->decref(cx);
+    if (!getOrCreate())
         return NULL;
-    }
 
-    reobj_->purge(cx);
-    if (!reobj_->init(cx, shared->getSource(), shared->getFlags())) {
-        shared->decref(cx);
+    if (!reobj_->init(cx, source, shared.getFlags()))
         return NULL;
-    }
-    reobj_->setPrivate(shared.get());
 
+    reobj_->setPrivate(&shared);
     return reobj_;
 }
 
 RegExpObject *
-RegExpObjectBuilder::build(JSLinearString *source, RegExpFlag flags)
+RegExpObjectBuilder::build(JSAtom *source, RegExpFlag flags)
 {
     if (!getOrCreate())
         return NULL;
 
-    reobj_->purge(cx);
     return reobj_->init(cx, source, flags) ? reobj_ : NULL;
 }
 
 RegExpObject *
 RegExpObjectBuilder::clone(RegExpObject *other, RegExpObject *proto)
 {
     if (!getOrCreateClone(proto))
         return NULL;
@@ -155,18 +134,17 @@ RegExpObjectBuilder::clone(RegExpObject 
         RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
         return build(other->getSource(), newFlags);
     }
 
     RegExpShared *toShare = other->getShared(cx);
     if (!toShare)
         return NULL;
 
-    toShare->incref(cx);
-    return build(AlreadyIncRefed<RegExpShared>(toShare));
+    return build(other->getSource(), *toShare);
 }
 
 /* MatchPairs */
 
 MatchPairs *
 MatchPairs::create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount)
 {
     void *mem = alloc.alloc(calculateSize(backingPairCount));
@@ -185,198 +163,17 @@ MatchPairs::checkAgainst(size_t inputLen
         p.check();
         if (p.isUndefined())
             continue;
         JS_ASSERT(size_t(p.limit) <= inputLength);
     }
 #endif
 }
 
-RegExpRunStatus
-RegExpShared::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
-                      LifoAllocScope &allocScope, MatchPairs **output)
-{
-    const size_t origLength = length;
-    size_t backingPairCount = RegExpCode::getOutputSize(pairCount());
-
-    MatchPairs *matchPairs = MatchPairs::create(allocScope.alloc(), pairCount(), backingPairCount);
-    if (!matchPairs)
-        return RegExpRunStatus_Error;
-
-    /*
-     * |displacement| emulates sticky mode by matching from this offset
-     * into the char buffer and subtracting the delta off at the end.
-     */
-    size_t start = *lastIndex;
-    size_t displacement = 0;
-
-    if (sticky()) {
-        displacement = *lastIndex;
-        chars += displacement;
-        length -= displacement;
-        start = 0;
-    }
-
-    RegExpRunStatus status = code.execute(cx, chars, length, start,
-                                          matchPairs->buffer(), backingPairCount);
-
-    switch (status) {
-      case RegExpRunStatus_Error:
-        return status;
-      case RegExpRunStatus_Success_NotFound:
-        *output = matchPairs;
-        return status;
-      default:
-        JS_ASSERT(status == RegExpRunStatus_Success);
-    }
-
-    matchPairs->displace(displacement);
-    matchPairs->checkAgainst(origLength);
-
-    *lastIndex = matchPairs->pair(0).limit;
-    *output = matchPairs;
-
-    return RegExpRunStatus_Success;
-}
-
-RegExpShared *
-RegExpObject::createShared(JSContext *cx)
-{
-    JS_ASSERT(!maybeShared());
-    AlreadyIncRefed<RegExpShared> shared = RegExpShared::create(cx, getSource(), getFlags(), NULL);
-    if (!shared)
-        return NULL;
-
-    setPrivate(shared.get());
-    return shared.get();
-}
-
-RegExpRunStatus
-RegExpObject::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
-                      LifoAllocScope &allocScope, MatchPairs **output)
-{
-    RegExpShared *shared = getShared(cx);
-    if (!shared)
-        return RegExpRunStatus_Error;
-    return shared->execute(cx, chars, length, lastIndex, allocScope, output);
-}
-
-Shape *
-RegExpObject::assignInitialShape(JSContext *cx)
-{
-    JS_ASSERT(isRegExp());
-    JS_ASSERT(nativeEmpty());
-
-    JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
-    JS_STATIC_ASSERT(SOURCE_SLOT == LAST_INDEX_SLOT + 1);
-    JS_STATIC_ASSERT(GLOBAL_FLAG_SLOT == SOURCE_SLOT + 1);
-    JS_STATIC_ASSERT(IGNORE_CASE_FLAG_SLOT == GLOBAL_FLAG_SLOT + 1);
-    JS_STATIC_ASSERT(MULTILINE_FLAG_SLOT == IGNORE_CASE_FLAG_SLOT + 1);
-    JS_STATIC_ASSERT(STICKY_FLAG_SLOT == MULTILINE_FLAG_SLOT + 1);
-
-    /* The lastIndex property alone is writable but non-configurable. */
-    if (!addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lastIndexAtom),
-                         LAST_INDEX_SLOT, JSPROP_PERMANENT))
-    {
-        return NULL;
-    }
-
-    /* Remaining instance properties are non-writable and non-configurable. */
-    if (!addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.sourceAtom),
-                         SOURCE_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) ||
-        !addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.globalAtom),
-                         GLOBAL_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) ||
-        !addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.ignoreCaseAtom),
-                         IGNORE_CASE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) ||
-        !addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.multilineAtom),
-                         MULTILINE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY))
-    {
-        return NULL;
-    }
-
-    return addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.stickyAtom),
-                           STICKY_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY);
-}
-
-#if JS_HAS_XDR
-
-#include "jsxdrapi.h"
-
-JSBool
-js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp)
-{
-    JSString *source = 0;
-    uint32_t flagsword = 0;
-
-    if (xdr->mode == JSXDR_ENCODE) {
-        JS_ASSERT(objp);
-        RegExpObject &reobj = (*objp)->asRegExp();
-        source = reobj.getSource();
-        flagsword = reobj.getFlags();
-    }
-    if (!JS_XDRString(xdr, &source) || !JS_XDRUint32(xdr, &flagsword))
-        return false;
-    if (xdr->mode == JSXDR_DECODE) {
-        JSAtom *atom = js_AtomizeString(xdr->cx, source);
-        if (!atom)
-            return false;
-        RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx, atom, RegExpFlag(flagsword),
-                                                            NULL);
-        if (!reobj)
-            return false;
-
-        if (!reobj->clearParent(xdr->cx))
-            return false;
-        if (!reobj->clearType(xdr->cx))
-            return false;
-        *objp = reobj;
-    }
-    return true;
-}
-
-#else  /* !JS_HAS_XDR */
-
-#define js_XDRRegExpObject NULL
-
-#endif /* !JS_HAS_XDR */
-
-static void
-regexp_finalize(JSContext *cx, JSObject *obj)
-{
-    obj->asRegExp().finalize(cx);
-}
-
-static void
-regexp_trace(JSTracer *trc, JSObject *obj)
-{
-    if (trc->runtime->gcRunning)
-        obj->asRegExp().purge(trc->context);
-}
-
-Class js::RegExpClass = {
-    js_RegExp_str,
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
-    JS_PropertyStub,         /* addProperty */
-    JS_PropertyStub,         /* delProperty */
-    JS_PropertyStub,         /* getProperty */
-    JS_StrictPropertyStub,   /* setProperty */
-    JS_EnumerateStub,        /* enumerate */
-    JS_ResolveStub,
-    JS_ConvertStub,
-    regexp_finalize,
-    NULL,                    /* reserved0 */
-    NULL,                    /* checkAccess */
-    NULL,                    /* call */
-    NULL,                    /* construct */
-    js_XDRRegExpObject,
-    NULL,                    /* hasInstance */
-    regexp_trace
-};
+/* detail::RegExpCode */
 
 #if ENABLE_YARR_JIT
 void
 RegExpCode::reportYarrError(JSContext *cx, TokenStream *ts, ErrorCode error)
 {
     switch (error) {
       case JSC::Yarr::NoError:
         JS_NOT_REACHED("Called reportYarrError with value for no error");
@@ -437,16 +234,478 @@ RegExpCode::reportPCREError(JSContext *c
         JS_NOT_REACHED("Precondition violation: unknown PCRE error code.");
     }
 #undef REPORT
 }
 
 #endif /* ENABLE_YARR_JIT */
 
 bool
+RegExpCode::compile(JSContext *cx, JSLinearString &pattern, uintN *parenCount, RegExpFlag flags)
+{
+#if ENABLE_YARR_JIT
+    /* Parse the pattern. */
+    ErrorCode yarrError;
+    YarrPattern yarrPattern(pattern, bool(flags & IgnoreCaseFlag), bool(flags & MultilineFlag),
+                            &yarrError);
+    if (yarrError) {
+        reportYarrError(cx, NULL, yarrError);
+        return false;
+    }
+    *parenCount = yarrPattern.m_numSubpatterns;
+
+    /*
+     * The YARR JIT compiler attempts to compile the parsed pattern. If
+     * it cannot, it informs us via |codeBlock.isFallBack()|, in which
+     * case we have to bytecode compile it.
+     */
+
+#ifdef JS_METHODJIT
+    if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) {
+        JSC::ExecutableAllocator *execAlloc = cx->runtime->getExecutableAllocator(cx);
+        if (!execAlloc) {
+            js_ReportOutOfMemory(cx);
+            return false;
+        }
+
+        JSGlobalData globalData(execAlloc);
+        jitCompile(yarrPattern, &globalData, codeBlock);
+        if (!codeBlock.isFallBack())
+            return true;
+    }
+#endif
+
+    WTF::BumpPointerAllocator *bumpAlloc = cx->runtime->getBumpPointerAllocator(cx);
+    if (!bumpAlloc) {
+        js_ReportOutOfMemory(cx);
+        return false;
+    }
+
+    codeBlock.setFallBack(true);
+    byteCode = byteCompile(yarrPattern, bumpAlloc).get();
+    return true;
+#else /* !defined(ENABLE_YARR_JIT) */
+    int error = 0;
+    compiled = jsRegExpCompile(pattern.chars(), pattern.length(),
+                  ignoreCase() ? JSRegExpIgnoreCase : JSRegExpDoNotIgnoreCase,
+                  multiline() ? JSRegExpMultiline : JSRegExpSingleLine,
+                  parenCount, &error);
+    if (error) {
+        reportPCREError(cx, error);
+        return false;
+    }
+    return true;
+#endif
+}
+
+RegExpRunStatus
+RegExpCode::execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
+                    int *output, size_t outputCount)
+{
+    int result;
+#if ENABLE_YARR_JIT
+    (void) cx; /* Unused. */
+    if (codeBlock.isFallBack())
+        result = JSC::Yarr::interpret(byteCode, chars, start, length, output);
+    else
+        result = JSC::Yarr::execute(codeBlock, chars, start, length, output);
+#else
+    result = jsRegExpExecute(cx, compiled, chars, length, start, output, outputCount);
+#endif
+
+    if (result == -1)
+        return RegExpRunStatus_Success_NotFound;
+
+#if !ENABLE_YARR_JIT
+    if (result < 0) {
+        reportPCREError(cx, result);
+        return RegExpRunStatus_Error;
+    }
+#endif
+
+    JS_ASSERT(result >= 0);
+    return RegExpRunStatus_Success;
+}
+
+/* RegExpObject */
+
+static void
+regexp_trace(JSTracer *trc, JSObject *obj)
+{
+    if (trc->runtime->gcRunning)
+        obj->setPrivate(NULL);
+}
+
+Class js::RegExpClass = {
+    js_RegExp_str,
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
+    JS_PropertyStub,         /* addProperty */
+    JS_PropertyStub,         /* delProperty */
+    JS_PropertyStub,         /* getProperty */
+    JS_StrictPropertyStub,   /* setProperty */
+    JS_EnumerateStub,        /* enumerate */
+    JS_ResolveStub,
+    JS_ConvertStub,
+    NULL,                    /* finalize */
+    NULL,                    /* reserved0 */
+    NULL,                    /* checkAccess */
+    NULL,                    /* call */
+    NULL,                    /* construct */
+#if JS_HAS_XDR
+    js_XDRRegExpObject,
+#else
+    NULL
+#endif
+    NULL,                    /* hasInstance */
+    regexp_trace
+};
+
+RegExpShared::RegExpShared(RegExpFlag flags)
+  : parenCount(0), flags(flags), activeUseCount(0)
+{}
+
+RegExpObject *
+RegExpObject::create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
+                     RegExpFlag flags, TokenStream *tokenStream)
+{
+    RegExpFlag staticsFlags = res->getFlags();
+    return createNoStatics(cx, chars, length, RegExpFlag(flags | staticsFlags), tokenStream);
+}
+
+RegExpObject *
+RegExpObject::createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags,
+                              TokenStream *tokenStream)
+{
+    JSAtom *source = js_AtomizeChars(cx, chars, length);
+    if (!source)
+        return NULL;
+
+    return createNoStatics(cx, source, flags, tokenStream);
+}
+
+RegExpObject *
+RegExpObject::createNoStatics(JSContext *cx, JSAtom *source, RegExpFlag flags,
+                              TokenStream *tokenStream)
+{
+    if (!RegExpCode::checkSyntax(cx, tokenStream, source))
+        return NULL;
+
+    RegExpObjectBuilder builder(cx);
+    return builder.build(source, flags);
+}
+
+RegExpShared *
+RegExpObject::createShared(JSContext *cx)
+{
+    JS_ASSERT(!maybeShared());
+    RegExpShared *shared = cx->compartment->regExps.get(cx, getSource(), getFlags());
+    if (!shared)
+        return NULL;
+
+    setPrivate(shared);
+    return shared;
+}
+
+Shape *
+RegExpObject::assignInitialShape(JSContext *cx)
+{
+    JS_ASSERT(isRegExp());
+    JS_ASSERT(nativeEmpty());
+
+    JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
+    JS_STATIC_ASSERT(SOURCE_SLOT == LAST_INDEX_SLOT + 1);
+    JS_STATIC_ASSERT(GLOBAL_FLAG_SLOT == SOURCE_SLOT + 1);
+    JS_STATIC_ASSERT(IGNORE_CASE_FLAG_SLOT == GLOBAL_FLAG_SLOT + 1);
+    JS_STATIC_ASSERT(MULTILINE_FLAG_SLOT == IGNORE_CASE_FLAG_SLOT + 1);
+    JS_STATIC_ASSERT(STICKY_FLAG_SLOT == MULTILINE_FLAG_SLOT + 1);
+
+    /* The lastIndex property alone is writable but non-configurable. */
+    if (!addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lastIndexAtom),
+                         LAST_INDEX_SLOT, JSPROP_PERMANENT))
+    {
+        return NULL;
+    }
+
+    /* Remaining instance properties are non-writable and non-configurable. */
+    if (!addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.sourceAtom),
+                         SOURCE_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) ||
+        !addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.globalAtom),
+                         GLOBAL_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) ||
+        !addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.ignoreCaseAtom),
+                         IGNORE_CASE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) ||
+        !addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.multilineAtom),
+                         MULTILINE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY))
+    {
+        return NULL;
+    }
+
+    return addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.stickyAtom),
+                           STICKY_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY);
+}
+
+inline bool
+RegExpObject::init(JSContext *cx, JSAtom *source, RegExpFlag flags)
+{
+    if (nativeEmpty()) {
+        if (isDelegate()) {
+            if (!assignInitialShape(cx))
+                return false;
+        } else {
+            Shape *shape = assignInitialShape(cx);
+            if (!shape)
+                return false;
+            EmptyShape::insertInitialShape(cx, shape, getProto());
+        }
+        JS_ASSERT(!nativeEmpty());
+    }
+
+    DebugOnly<JSAtomState *> atomState = &cx->runtime->atomState;
+    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->lastIndexAtom))->slot() == LAST_INDEX_SLOT);
+    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->sourceAtom))->slot() == SOURCE_SLOT);
+    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->globalAtom))->slot() == GLOBAL_FLAG_SLOT);
+    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->ignoreCaseAtom))->slot() ==
+                                 IGNORE_CASE_FLAG_SLOT);
+    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->multilineAtom))->slot() ==
+                                 MULTILINE_FLAG_SLOT);
+    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->stickyAtom))->slot() == STICKY_FLAG_SLOT);
+
+    JS_ASSERT(!maybeShared());
+    zeroLastIndex();
+    setSource(source);
+    setGlobal(flags & GlobalFlag);
+    setIgnoreCase(flags & IgnoreCaseFlag);
+    setMultiline(flags & MultilineFlag);
+    setSticky(flags & StickyFlag);
+    return true;
+}
+
+RegExpRunStatus
+RegExpObject::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
+                      MatchPairs **output)
+{
+    RegExpShared *shared = getShared(cx);
+    if (!shared)
+        return RegExpRunStatus_Error;
+    return shared->execute(cx, chars, length, lastIndex, output);
+}
+
+JSFlatString *
+RegExpObject::toString(JSContext *cx) const
+{
+    JSAtom *src = getSource();
+    StringBuffer sb(cx);
+    if (size_t len = src->length()) {
+        if (!sb.reserve(len + 2))
+            return NULL;
+        sb.infallibleAppend('/');
+        sb.infallibleAppend(src->chars(), len);
+        sb.infallibleAppend('/');
+    } else {
+        if (!sb.append("/(?:)/"))
+            return NULL;
+    }
+    if (global() && !sb.append('g'))
+        return NULL;
+    if (ignoreCase() && !sb.append('i'))
+        return NULL;
+    if (multiline() && !sb.append('m'))
+        return NULL;
+    if (sticky() && !sb.append('y'))
+        return NULL;
+
+    return sb.finishString();
+}
+
+/* RegExpShared */
+
+bool
+RegExpShared::compile(JSContext *cx, JSAtom *source)
+{
+    if (!sticky())
+        return code.compile(cx, *source, &parenCount, getFlags());
+
+    /*
+     * The sticky case we implement hackily by prepending a caret onto the front
+     * and relying on |::execute| to pseudo-slice the string when it sees a sticky regexp.
+     */
+    static const jschar prefix[] = {'^', '(', '?', ':'};
+    static const jschar postfix[] = {')'};
+
+    using mozilla::ArrayLength;
+    StringBuffer sb(cx);
+    if (!sb.reserve(ArrayLength(prefix) + source->length() + ArrayLength(postfix)))
+        return false;
+    sb.infallibleAppend(prefix, ArrayLength(prefix));
+    sb.infallibleAppend(source->chars(), source->length());
+    sb.infallibleAppend(postfix, ArrayLength(postfix));
+
+    JSAtom *fakeySource = sb.finishAtom();
+    if (!fakeySource)
+        return false;
+    return code.compile(cx, *fakeySource, &parenCount, getFlags());
+}
+
+RegExpRunStatus
+RegExpShared::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
+                      MatchPairs **output)
+{
+    const size_t origLength = length;
+    size_t backingPairCount = RegExpCode::getOutputSize(pairCount());
+
+    LifoAlloc &alloc = cx->tempLifoAlloc();
+    MatchPairs *matchPairs = MatchPairs::create(alloc, pairCount(), backingPairCount);
+    if (!matchPairs)
+        return RegExpRunStatus_Error;
+
+    /*
+     * |displacement| emulates sticky mode by matching from this offset
+     * into the char buffer and subtracting the delta off at the end.
+     */
+    size_t start = *lastIndex;
+    size_t displacement = 0;
+
+    if (sticky()) {
+        displacement = *lastIndex;
+        chars += displacement;
+        length -= displacement;
+        start = 0;
+    }
+
+    RegExpRunStatus status = code.execute(cx, chars, length, start,
+                                          matchPairs->buffer(), backingPairCount);
+
+    switch (status) {
+      case RegExpRunStatus_Error:
+        return status;
+      case RegExpRunStatus_Success_NotFound:
+        *output = matchPairs;
+        return status;
+      default:
+        JS_ASSERT(status == RegExpRunStatus_Success);
+    }
+
+    matchPairs->displace(displacement);
+    matchPairs->checkAgainst(origLength);
+
+    *lastIndex = matchPairs->pair(0).limit;
+    *output = matchPairs;
+
+    return RegExpRunStatus_Success;
+}
+
+/* RegExpCompartment */
+
+RegExpCompartment::RegExpCompartment(JSRuntime *rt)
+  : map_(rt)
+{}
+
+RegExpCompartment::~RegExpCompartment()
+{
+    map_.empty();
+}
+
+bool
+RegExpCompartment::init(JSContext *cx)
+{
+    if (!map_.init()) {
+        js_ReportOutOfMemory(cx);
+        return false;
+    }
+
+    return true;
+}
+
+void
+RegExpCompartment::purge()
+{
+    for (Map::Enum e(map_); !e.empty(); e.popFront()) {
+        RegExpShared *shared = e.front().value;
+        if (shared->activeUseCount == 0) {
+            Foreground::delete_(shared);
+            e.removeFront();
+        }
+    }
+}
+
+inline RegExpShared *
+RegExpCompartment::get(JSContext *cx, JSAtom *keyAtom, JSAtom *source, RegExpFlag flags, Type type)
+{
+    DebugOnly<size_t> gcNumberBefore = cx->runtime->gcNumber;
+
+    Key key(keyAtom, flags, type);
+    Map::AddPtr p = map_.lookupForAdd(key);
+    if (p)
+        return p->value;
+
+    RegExpShared *shared = cx->runtime->new_<RegExpShared>(flags);
+    if (!shared || !shared->compile(cx, source))
+        goto error;
+
+    /*
+     * The compilation path only mallocs so cannot GC. Thus, it is safe to add
+     * the regexp directly.
+     */
+    JS_ASSERT(cx->runtime->gcNumber == gcNumberBefore);
+
+    if (!map_.add(p, key, shared))
+        goto error;
+
+    return shared;
+
+  error:
+    Foreground::delete_(shared);
+    js_ReportOutOfMemory(cx);
+    return NULL;
+}
+
+RegExpShared *
+RegExpCompartment::get(JSContext *cx, JSAtom *source, RegExpFlag flags)
+{
+    return get(cx, source, source, flags, Normal);
+}
+
+RegExpShared *
+RegExpCompartment::getHack(JSContext *cx, JSAtom *source, JSAtom *hackedSource, RegExpFlag flags)
+{
+    return get(cx, source, hackedSource, flags, Hack);
+}
+
+RegExpShared *
+RegExpCompartment::lookupHack(JSContext *cx, JSAtom *source, RegExpFlag flags)
+{
+    if (Map::Ptr p = map_.lookup(Key(source, flags, Hack)))
+        return p->value;
+    return NULL;
+}
+
+RegExpShared *
+RegExpCompartment::get(JSContext *cx, JSAtom *atom, JSString *opt)
+{
+    RegExpFlag flags = RegExpFlag(0);
+    if (opt && !ParseRegExpFlags(cx, opt, &flags))
+        return NULL;
+
+    return get(cx, atom, flags);
+}
+
+/* Functions */
+
+JSObject *
+js::CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
+{
+    JS_ASSERT(obj->isRegExp());
+    JS_ASSERT(proto->isRegExp());
+
+    RegExpObjectBuilder builder(cx);
+    return builder.clone(&obj->asRegExp(), &proto->asRegExp());
+}
+
+bool
 js::ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut)
 {
     size_t n = flagStr->length();
     const jschar *s = flagStr->getChars(cx);
     if (!s)
         return false;
 
     *flagsOut = RegExpFlag(0);
@@ -473,106 +732,41 @@ js::ParseRegExpFlags(JSContext *cx, JSSt
             return false;
           }
         }
 #undef HANDLE_FLAG
     }
     return true;
 }
 
-RegExpShared *
-RegExpShared::createUncached(JSContext *cx, JSLinearString *source, RegExpFlag flags,
-                             TokenStream *tokenStream)
-{
-    RegExpShared *shared = cx->new_<RegExpShared>(source, flags);
-    if (!shared)
-        return NULL;
-
-    if (!shared->compile(cx, tokenStream)) {
-        Foreground::delete_(shared);
-        return NULL;
-    }
-
-    return shared;
-}
-
-AlreadyIncRefed<RegExpShared>
-RegExpShared::createTestOptimized(JSContext *cx, JSAtom *cacheKey, RegExpFlag flags)
-{
-    using namespace detail;
-    typedef AlreadyIncRefed<RegExpShared> RetType;
+#if JS_HAS_XDR
+# include "jsxdrapi.h"
 
-    RetType cached;
-    if (!cacheLookup(cx, cacheKey, flags, RegExpCache_TestOptimized, &cached))
-        return RetType(NULL);
-
-    if (cached)
-        return cached;
-
-    /* Strip off the greedy star characters, create a new RegExpShared, and cache. */
-    JS_ASSERT(cacheKey->length() > JS_ARRAY_LENGTH(GreedyStarChars));
-    JSDependentString *stripped =
-      JSDependentString::new_(cx, cacheKey, cacheKey->chars() + JS_ARRAY_LENGTH(GreedyStarChars),
-                              cacheKey->length() - JS_ARRAY_LENGTH(GreedyStarChars));
-    if (!stripped)
-        return RetType(NULL);
-
-    RegExpShared *shared = createUncached(cx, cacheKey, flags, NULL);
-    if (!shared)
-        return RetType(NULL);
-
-    if (!cacheInsert(cx, cacheKey, RegExpCache_TestOptimized, *shared)) {
-        shared->decref(cx);
-        return RetType(NULL);
-    }
-
-    return RetType(shared);
-}
+JSBool
+js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp)
+{
+    JSAtom *source = 0;
+    uint32_t flagsword = 0;
 
-AlreadyIncRefed<RegExpShared>
-RegExpShared::create(JSContext *cx, JSLinearString *str, JSString *opt, TokenStream *ts)
-{
-    if (!opt)
-        return create(cx, str, RegExpFlag(0), ts);
-
-    RegExpFlag flags = RegExpFlag(0);
-    if (!ParseRegExpFlags(cx, opt, &flags))
-        return AlreadyIncRefed<RegExpShared>(NULL);
-
-    return create(cx, str, flags, ts);
-}
-
-JSObject *
-js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
-{
-    JS_ASSERT(obj->isRegExp());
-    JS_ASSERT(proto->isRegExp());
-
-    RegExpObjectBuilder builder(cx);
-    return builder.clone(&obj->asRegExp(), &proto->asRegExp());
-}
+    if (xdr->mode == JSXDR_ENCODE) {
+        JS_ASSERT(objp);
+        RegExpObject &reobj = (*objp)->asRegExp();
+        source = reobj.getSource();
+        flagsword = reobj.getFlags();
+    }
+    if (!js_XDRAtom(xdr, &source) || !JS_XDRUint32(xdr, &flagsword))
+        return false;
+    if (xdr->mode == JSXDR_DECODE) {
+        RegExpFlag flags = RegExpFlag(flagsword);
+        RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx, source, flags, NULL);
+        if (!reobj)
+            return false;
 
-JSFlatString *
-RegExpObject::toString(JSContext *cx) const
-{
-    JSLinearString *src = getSource();
-    StringBuffer sb(cx);
-    if (size_t len = src->length()) {
-        if (!sb.reserve(len + 2))
-            return NULL;
-        sb.infallibleAppend('/');
-        sb.infallibleAppend(src->chars(), len);
-        sb.infallibleAppend('/');
-    } else {
-        if (!sb.append("/(?:)/"))
-            return NULL;
+        if (!reobj->clearParent(xdr->cx))
+            return false;
+        if (!reobj->clearType(xdr->cx))
+            return false;
+        *objp = reobj;
     }
-    if (global() && !sb.append('g'))
-        return NULL;
-    if (ignoreCase() && !sb.append('i'))
-        return NULL;
-    if (multiline() && !sb.append('m'))
-        return NULL;
-    if (sticky() && !sb.append('y'))
-        return NULL;
+    return true;
+}
+#endif /* !JS_HAS_XDR */
 
-    return sb.finishString();
-}
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -51,16 +51,36 @@
 #include "yarr/Yarr.h"
 #if ENABLE_YARR_JIT
 #include "yarr/YarrJIT.h"
 #include "yarr/YarrSyntaxChecker.h"
 #else
 #include "yarr/pcre/pcre.h"
 #endif
 
+/*
+ * JavaScript Regular Expressions
+ *
+ * There are several engine concepts associated with a single logical regexp:
+ *
+ *   RegExpObject - The JS-visible object whose .[[Class]] equals "RegExp"
+ *
+ *   RegExpShared - The compiled representation of the regexp.
+ *
+ *   RegExpCode - The low-level implementation jit details.
+ *
+ *   RegExpCompartment - Owns all RegExpShared instances in a compartment.
+ *
+ * To save memory, a RegExpShared is not created for a RegExpObject until it is
+ * needed for execution. When a RegExpShared needs to be created, it is looked
+ * up in a per-compartment table to allow reuse between objects. Lastly, on
+ * GC, every RegExpShared (that is not active on the callstack) is discarded.
+ * Because of the last point, any code using a RegExpShared (viz., by executing
+ * a regexp) must indicate the RegExpShared is active via RegExpShared::Guard.
+ */
 namespace js {
 
 enum RegExpRunStatus
 {
     RegExpRunStatus_Error,
     RegExpRunStatus_Success,
     RegExpRunStatus_Success_NotFound
 };
@@ -79,160 +99,129 @@ class RegExpObject : public JSObject
   public:
     static const uintN RESERVED_SLOTS = 6;
 
     /*
      * Note: The regexp statics flags are OR'd into the provided flags,
      * so this function is really meant for object creation during code
      * execution, as opposed to during something like XDR.
      */
-    static inline RegExpObject *
-    create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length, RegExpFlag flags,
-           TokenStream *tokenStream);
+    static RegExpObject *
+    create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
+           RegExpFlag flags, TokenStream *ts);
 
-    static inline RegExpObject *
+    static RegExpObject *
     createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags,
-                    TokenStream *tokenStream);
+                    TokenStream *ts);
 
-    static inline RegExpObject *
-    createNoStatics(JSContext *cx, JSAtom *atom, RegExpFlag flags, TokenStream *tokenStream);
-
-    /* Note: fallible. */
-    JSFlatString *toString(JSContext *cx) const;
+    static RegExpObject *
+    createNoStatics(JSContext *cx, JSAtom *atom, RegExpFlag flags, TokenStream *ts);
 
     /*
      * Run the regular expression over the input text.
      *
      * Results are placed in |output| as integer pairs. For eaxmple,
      * |output[0]| and |output[1]| represent the text indices that make
      * up the "0" (whole match) pair. Capturing parens will result in
      * more output.
      *
      * N.B. it's the responsibility of the caller to hook the |output|
      * into the |RegExpStatics| appropriately, if necessary.
      */
-    RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
-                            LifoAllocScope &allocScope, MatchPairs **output);
+    RegExpRunStatus
+    execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
+            MatchPairs **output);
 
     /* Accessors. */
 
     const Value &getLastIndex() const {
         return getSlot(LAST_INDEX_SLOT);
     }
     inline void setLastIndex(const Value &v);
     inline void setLastIndex(double d);
     inline void zeroLastIndex();
 
-    JSLinearString *getSource() const {
-        return &getSlot(SOURCE_SLOT).toString()->asLinear();
+    JSFlatString *toString(JSContext *cx) const;
+
+    JSAtom *getSource() const {
+        return &getSlot(SOURCE_SLOT).toString()->asAtom();
     }
-    inline void setSource(JSLinearString *source);
+    inline void setSource(JSAtom *source);
 
     RegExpFlag getFlags() const {
         uintN flags = 0;
         flags |= global() ? GlobalFlag : 0;
         flags |= ignoreCase() ? IgnoreCaseFlag : 0;
         flags |= multiline() ? MultilineFlag : 0;
         flags |= sticky() ? StickyFlag : 0;
         return RegExpFlag(flags);
     }
 
-    inline bool startsWithAtomizedGreedyStar() const;
-
-    /* JIT only. */
-
-    inline size_t *addressOfPrivateRefCount() const;
-
     /* Flags. */
 
     inline void setIgnoreCase(bool enabled);
     inline void setGlobal(bool enabled);
     inline void setMultiline(bool enabled);
     inline void setSticky(bool enabled);
     bool ignoreCase() const { return getSlot(IGNORE_CASE_FLAG_SLOT).toBoolean(); }
     bool global() const     { return getSlot(GLOBAL_FLAG_SLOT).toBoolean(); }
     bool multiline() const  { return getSlot(MULTILINE_FLAG_SLOT).toBoolean(); }
     bool sticky() const     { return getSlot(STICKY_FLAG_SLOT).toBoolean(); }
 
-    inline void finalize(JSContext *cx);
-
-    /* Clear out lazy |RegExpShared|. */
-    inline void purge(JSContext *x);
-
-    RegExpShared &shared() const {
-        JS_ASSERT(JSObject::getPrivate() != NULL);
-        return *static_cast<RegExpShared *>(JSObject::getPrivate());
-    }
-
-    RegExpShared *maybeShared() {
-        return static_cast<RegExpShared *>(JSObject::getPrivate());
-    }
-
-    RegExpShared *getShared(JSContext *cx) {
-        if (RegExpShared *shared = maybeShared())
-            return shared;
-        return createShared(cx);
-    }
+    inline RegExpShared &shared() const;
+    inline RegExpShared *maybeShared();
+    inline RegExpShared *getShared(JSContext *cx);
 
   private:
     friend class RegExpObjectBuilder;
-    friend class RegExpMatcher;
-
-    inline bool init(JSContext *cx, JSLinearString *source, RegExpFlag flags);
-
-    /*
-     * Precondition: the syntax for |source| has already been validated.
-     * Side effect: sets the private field.
-     */
-    RegExpShared *createShared(JSContext *cx);
-
-    friend bool ResetRegExpObject(JSContext *, RegExpObject *, JSLinearString *, RegExpFlag);
-    friend bool ResetRegExpObject(JSContext *, RegExpObject *, AlreadyIncRefed<RegExpShared>);
 
     /*
      * Compute the initial shape to associate with fresh RegExp objects,
      * encoding their initial properties. Return the shape after
      * changing this regular expression object's last property to it.
      */
     Shape *assignInitialShape(JSContext *cx);
 
+    inline bool init(JSContext *cx, JSAtom *source, RegExpFlag flags);
+
+    /*
+     * Precondition: the syntax for |source| has already been validated.
+     * Side effect: sets the private field.
+     */
+    RegExpShared *createShared(JSContext *cx);
+
     RegExpObject() MOZ_DELETE;
     RegExpObject &operator=(const RegExpObject &reo) MOZ_DELETE;
-}; /* class RegExpObject */
+};
 
-/* Either builds a new RegExpObject or re-initializes an existing one. */
 class RegExpObjectBuilder
 {
     JSContext       *cx;
     RegExpObject    *reobj_;
 
     bool getOrCreate();
     bool getOrCreateClone(RegExpObject *proto);
 
-    friend class RegExpMatcher;
-
   public:
-    RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj = NULL)
-      : cx(cx), reobj_(reobj)
-    { }
+    RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj = NULL);
 
     RegExpObject *reobj() { return reobj_; }
 
-    RegExpObject *build(JSLinearString *str, RegExpFlag flags);
-    RegExpObject *build(AlreadyIncRefed<RegExpShared> shared);
+    RegExpObject *build(JSAtom *source, RegExpFlag flags);
+    RegExpObject *build(JSAtom *source, RegExpShared &shared);
 
     /* Perform a VM-internal clone. */
     RegExpObject *clone(RegExpObject *other, RegExpObject *proto);
 };
 
+JSObject *
+CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto);
+
 namespace detail {
 
-static const jschar GreedyStarChars[] = {'.', '*'};
-
-/* Abstracts away the gross |RegExpShared| backend details. */
 class RegExpCode
 {
 #if ENABLE_YARR_JIT
     typedef JSC::Yarr::BytecodePattern BytecodePattern;
     typedef JSC::Yarr::ErrorCode ErrorCode;
     typedef JSC::Yarr::JSGlobalData JSGlobalData;
     typedef JSC::Yarr::YarrCodeBlock YarrCodeBlock;
     typedef JSC::Yarr::YarrPattern YarrPattern;
@@ -289,202 +278,168 @@ class RegExpCode
     static size_t getOutputSize(size_t pairCount) {
 #if ENABLE_YARR_JIT
         return pairCount * 2;
 #else
         return pairCount * 3; /* Should be x2, but PCRE has... needs. */
 #endif
     }
 
-    inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount,
-                        RegExpFlag flags);
+    bool compile(JSContext *cx, JSLinearString &pattern, uintN *parenCount, RegExpFlag flags);
 
 
-    inline RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
-                                   int *output, size_t outputCount);
-};
-
-enum RegExpCacheKind
-{
-    RegExpCache_TestOptimized,
-    RegExpCache_ExecCapable
-};
-
-class RegExpCacheValue
-{
-    union {
-        RegExpShared    *shared_;
-        uintptr_t       bits;
-    };
-
-  public:
-    RegExpCacheValue() : shared_(NULL) {}
-
-    RegExpCacheValue(RegExpShared &shared, RegExpCacheKind kind) {
-        reset(shared, kind);
-    }
-
-    RegExpCacheKind kind() const {
-        return (bits & 0x1)
-                ? RegExpCache_TestOptimized
-                : RegExpCache_ExecCapable;
-    }
-
-    RegExpShared &shared() {
-        return *reinterpret_cast<RegExpShared *>(bits & ~uintptr_t(1));
-    }
-
-    void reset(RegExpShared &shared, RegExpCacheKind kind) {
-        shared_ = &shared;
-        if (kind == RegExpCache_TestOptimized)
-            bits |= 0x1;
-        JS_ASSERT(this->kind() == kind);
-    }
+    RegExpRunStatus
+    execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
+            int *output, size_t outputCount);
 };
 
-} /* namespace detail */
+}  /* namespace detail */
 
-/*
- * The "meat" of the builtin regular expression objects: it contains the
- * mini-program that represents the source of the regular expression.
- * Excepting refcounts, this is an immutable datastructure after
- * compilation.
- *
- * Note: refCount cannot overflow because that would require more
- * referring regexp objects than there is space for in addressable
- * memory.
- */
+/* The compiled representation of a regexp. */
 class RegExpShared
 {
-    typedef detail::RegExpCode       RegExpCode;
-    typedef detail::RegExpCacheKind  RegExpCacheKind;
-    typedef detail::RegExpCacheValue RegExpCacheValue;
+    friend class RegExpCompartment;
 
-    RegExpCode     code;
-    JSLinearString *source;
-    size_t         refCount;
-    uintN          parenCount;
-    RegExpFlag     flags;
+    detail::RegExpCode code;
+    uintN              parenCount;
+    RegExpFlag         flags;
+    size_t             activeUseCount;
 
-  private:
-    RegExpShared(JSLinearString *source, RegExpFlag flags)
-      : source(source), refCount(1), parenCount(0), flags(flags)
-    { }
+    bool compile(JSContext *cx, JSAtom *source);
 
+    RegExpShared(RegExpFlag flags);
     JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
 
-    bool compile(JSContext *cx, TokenStream *ts);
-    static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount);
-
-    static RegExpShared *
-    createUncached(JSContext *cx, JSLinearString *source, RegExpFlag flags,
-                   TokenStream *tokenStream);
-
-    static bool cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
-                            RegExpCacheKind kind, AlreadyIncRefed<RegExpShared> *result);
-    static bool cacheInsert(JSContext *cx, JSAtom *atom,
-                            RegExpCacheKind kind, RegExpShared &shared);
-
   public:
-    static AlreadyIncRefed<RegExpShared>
-    create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts);
+    /*
+     * Extend the lifetime of a given RegExpShared to at least the lifetime of
+     * the Guard object. See Regular Expression comment at the top.
+     */
+    class Guard {
+        RegExpShared *re_;
+        Guard(const Guard &) MOZ_DELETE;
+        void operator=(const Guard &) MOZ_DELETE;
+      public:
+        Guard() : re_(NULL) {}
+        Guard(RegExpShared &re) : re_(&re) {
+            re_->activeUseCount++;
+        }
+        void init(RegExpShared &re) {
+            JS_ASSERT(!re_);
+            re_ = &re;
+            re_->activeUseCount++;
+        }
+        ~Guard() {
+            if (re_) {
+                JS_ASSERT(re_->activeUseCount > 0);
+                re_->activeUseCount--;
+            }
+        }
+        bool initialized() const { return !!re_; }
+        RegExpShared *operator->() { JS_ASSERT(initialized()); return re_; }
+        RegExpShared &operator*() { JS_ASSERT(initialized()); return *re_; }
+    };
 
-    static AlreadyIncRefed<RegExpShared>
-    create(JSContext *cx, JSLinearString *source, JSString *flags, TokenStream *ts);
-
-    static AlreadyIncRefed<RegExpShared>
-    createTestOptimized(JSContext *cx, JSAtom *originalSource, RegExpFlag flags);
+    /* Primary interface: run this regular expression on the given string. */
 
-    RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
-                            LifoAllocScope &allocScope, MatchPairs **output);
-
-    /* Mutators */
-
-    void incref(JSContext *cx);
-    void decref(JSContext *cx);
-
-    /* For JIT access. */
-    size_t *addressOfRefCount() { return &refCount; }
+    RegExpRunStatus
+    execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
+            MatchPairs **output);
 
     /* Accessors */
 
-    JSLinearString *getSource() const   { return source; }
     size_t getParenCount() const        { return parenCount; }
 
     /* Accounts for the "0" (whole match) pair. */
     size_t pairCount() const            { return parenCount + 1; }
 
     RegExpFlag getFlags() const         { return flags; }
-    bool ignoreCase() const { return flags & IgnoreCaseFlag; }
-    bool global() const     { return flags & GlobalFlag; }
-    bool multiline() const  { return flags & MultilineFlag; }
-    bool sticky() const     { return flags & StickyFlag; }
+    bool ignoreCase() const             { return flags & IgnoreCaseFlag; }
+    bool global() const                 { return flags & GlobalFlag; }
+    bool multiline() const              { return flags & MultilineFlag; }
+    bool sticky() const                 { return flags & StickyFlag; }
 };
 
-/*
- * Wraps the RegExpObject's internals in a recount-safe interface for
- * use in RegExp execution. This is used in situations where we'd like
- * to avoid creating a full-fledged RegExpObject. This interface is
- * provided in lieu of exposing the RegExpShared directly.
- *
- * Note: this exposes precisely the execute interface of a RegExpObject.
- */
-class RegExpMatcher
+class RegExpCompartment
 {
-    JSContext                  *cx_;
-    AutoRefCount<RegExpShared> shared_;
+    enum Type { Normal = 0x0, Hack = 0x1 };
+
+    struct Key {
+        JSAtom *atom;
+        uint16_t flag;
+        uint16_t type;
+        Key() {}
+        Key(JSAtom *atom, RegExpFlag flag, Type type)
+          : atom(atom), flag(flag), type(type) {}
+        typedef Key Lookup;
+        static HashNumber hash(const Lookup &l) {
+            return DefaultHasher<JSAtom *>::hash(l.atom) ^ (l.flag << 1) ^ l.type;
+        }
+        static bool match(Key l, Key r) {
+            return l.atom == r.atom && l.flag == r.flag && l.type == r.type;
+        }
+    };
+
+    typedef HashMap<Key, RegExpShared *, Key, RuntimeAllocPolicy> Map;
+    Map map_;
+
+    RegExpShared *get(JSContext *cx, JSAtom *key, JSAtom *source, RegExpFlag flags, Type type);
 
   public:
-    explicit RegExpMatcher(JSContext *cx) : cx_(cx), shared_(cx) {
-        JS_ASSERT(!initialized());
-    }
+    RegExpCompartment(JSRuntime *rt);
+    ~RegExpCompartment();
+
+    bool init(JSContext *cx);
+    void purge();
+
+    /* Return a regexp corresponding to the given (source, flags) pair. */
+    RegExpShared *get(JSContext *cx, JSAtom *source, RegExpFlag flags);
+
+    /* Like 'get', but compile 'maybeOpt' (if non-null). */
+    RegExpShared *get(JSContext *cx, JSAtom *source, JSString *maybeOpt);
 
-    bool initialized() const {
-        return !shared_.null();
-    }
-    bool global() const {
-        return shared_->global();
-    }
-    bool sticky() const {
-        return shared_->sticky();
-    }
+    /*
+     * A 'hacked' RegExpShared is one where the input 'source' doesn't match
+     * what is actually compiled in the regexp. To compile a hacked regexp,
+     * getHack may be called providing both the original 'source' and the
+     * 'hackedSource' which should actually be compiled. For a given 'source'
+     * there may only ever be one corresponding 'hackedSource'. Thus, we assume
+     * there is some single pure function mapping 'source' to 'hackedSource'
+     * that is always respected in calls to getHack. Note that this restriction
+     * only applies to 'getHack': a single 'source' value may be passed to both
+     * 'get' and 'getHack'.
+     */
+    RegExpShared *getHack(JSContext *cx, JSAtom *source, JSAtom *hackedSource, RegExpFlag flags);
 
-    inline void init(NeedsIncRef<RegExpShared> shared);
-    inline bool init(JSLinearString *patstr, JSString *opt);
-    bool initWithTestOptimized(RegExpObject &reobj);
-
-    RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
-                            LifoAllocScope &allocScope, MatchPairs **output) const {
-        JS_ASSERT(initialized());
-        return shared_->execute(cx, chars, length, lastIndex, allocScope, output);
-    }
+    /*
+     * To avoid atomizing 'hackedSource', callers may call 'lookupHack',
+     * passing only the original 'source'. Due to the abovementioned unique
+     * mapping property, 'hackedSource' is unambiguous.
+     */
+    RegExpShared *lookupHack(JSContext *cx, JSAtom *source, RegExpFlag flags);
 };
 
 /*
  * Parse regexp flags. Report an error and return false if an invalid
  * sequence of flags is encountered (repeat/invalid flag).
  *
  * N.B. flagStr must be rooted.
  */
 bool
 ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut);
 
-inline bool
-IsRegExpMetaChar(jschar c);
-
-inline bool
-CheckRegExpSyntax(JSContext *cx, JSLinearString *str)
-{
-    return detail::RegExpCode::checkSyntax(cx, NULL, str);
-}
-
+/*
+ * Assuming ObjectClassIs(obj, ESClass_RegExp), return obj's RegExpShared.
+ *
+ * Beware: this RegExpShared can be owned by a compartment other than
+ * cx->compartment. Normal RegExpShared::Guard (which is necessary anyways)
+ * will protect the object but it is important not to assign the return value
+ * to be the private of any RegExpObject.
+ */
 inline RegExpShared *
 RegExpToShared(JSContext *cx, JSObject &obj);
 
 } /* namespace js */
 
-extern JS_FRIEND_API(JSObject *)
-js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto);
-
 JSBool
 js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp);
 
 #endif
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -207,16 +207,41 @@ StackFrame::pcQuadratic(const ContextSta
         return regs.pc;
     }
 
     if (!next)
         next = seg.computeNextFrame(this);
     return next->prevpc(pinlined);
 }
 
+void
+StackFrame::mark(JSTracer *trc)
+{
+    /*
+     * Normally we would use MarkRoot here, except that generators also take
+     * this path. However, generators use a special write barrier when the stack
+     * frame is copied to the floating frame. Therefore, no barrier is needed.
+     */
+    gc::MarkObjectUnbarriered(trc, &scopeChain(), "scope chain");
+    if (isDummyFrame())
+        return;
+    if (hasArgsObj())
+        gc::MarkObjectUnbarriered(trc, &argsObj(), "arguments");
+    if (isFunctionFrame()) {
+        gc::MarkObjectUnbarriered(trc, fun(), "fun");
+        if (isEvalFrame())
+            gc::MarkScriptUnbarriered(trc, script(), "eval script");
+    } else {
+        gc::MarkScriptUnbarriered(trc, script(), "script");
+    }
+    if (IS_GC_MARKING_TRACER(trc))
+        script()->compartment()->active = true;
+    gc::MarkValueUnbarriered(trc, returnValue(), "rval");
+}
+
 /*****************************************************************************/
 
 bool
 StackSegment::contains(const StackFrame *fp) const
 {
     /* NB: this depends on the continuity of segments in memory. */
     return (Value *)fp >= slotsBegin() && (Value *)fp <= (Value *)maybefp();
 }
@@ -378,16 +403,61 @@ StackSpace::containingSegment(const Stac
         if (s->contains(target))
             return *s;
     }
     JS_NOT_REACHED("frame not in stack space");
     return *(StackSegment *)NULL;
 }
 
 void
+StackSpace::markFrameSlots(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc)
+{
+    Value *slotsBegin = fp->slots();
+
+    if (!fp->isScriptFrame()) {
+        JS_ASSERT(fp->isDummyFrame());
+        gc::MarkRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
+        return;
+    }
+
+    /* If it's a scripted frame, we should have a pc. */
+    JS_ASSERT(pc);
+
+    JSScript *script = fp->script();
+    if (!script->hasAnalysis() || !script->analysis()->ranLifetimes()) {
+        gc::MarkRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
+        return;
+    }
+
+    /*
+     * If the JIT ran a lifetime analysis, then it may have left garbage in the
+     * slots considered not live. We need to avoid marking them. Additionally,
+     * in case the analysis information is thrown out later, we overwrite these
+     * dead slots with valid values so that future GCs won't crash. Analysis
+     * results are thrown away during the sweeping phase, so we always have at
+     * least one GC to do this.
+     */
+    analyze::AutoEnterAnalysis aea(script->compartment());
+    analyze::ScriptAnalysis *analysis = script->analysis();
+    uint32_t offset = pc - script->code;
+    Value *fixedEnd = slotsBegin + script->nfixed;
+    for (Value *vp = slotsBegin; vp < fixedEnd; vp++) {
+        uint32_t slot = analyze::LocalSlot(script, vp - slotsBegin);
+
+        /* Will this slot be synced by the JIT? */
+        if (!analysis->trackSlot(slot) || analysis->liveness(slot).live(offset))
+            gc::MarkRoot(trc, *vp, "vm_stack");
+        else
+            *vp = UndefinedValue();
+    }
+
+    gc::MarkRootRange(trc, fixedEnd, slotsEnd, "vm_stack");
+}
+
+void
 StackSpace::mark(JSTracer *trc)
 {
     /*
      * JIT code can leave values in an incoherent (i.e., unsafe for precise
      * marking) state, hence MarkStackRangeConservatively.
      */
 
     /* NB: this depends on the continuity of segments in memory. */
@@ -397,25 +467,31 @@ StackSpace::mark(JSTracer *trc)
          * A segment describes a linear region of memory that contains a stack
          * of native and interpreted calls. For marking purposes, though, we
          * only need to distinguish between frames and values and mark
          * accordingly. Since native calls only push values on the stack, we
          * can effectively lump them together and just iterate over interpreted
          * calls. Thus, marking can view the stack as the regex:
          *   (segment slots (frame slots)*)*
          * which gets marked in reverse order.
-         *
          */
         Value *slotsEnd = nextSegEnd;
+        jsbytecode *pc = seg->maybepc();
         for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev()) {
-            MarkStackRangeConservatively(trc, fp->slots(), slotsEnd);
-            js_TraceStackFrame(trc, fp);
+            /* Mark from fp->slots() to slotsEnd. */
+            markFrameSlots(trc, fp, slotsEnd, pc);
+
+            fp->mark(trc);
             slotsEnd = (Value *)fp;
+
+            JSInlinedSite *site;
+            pc = fp->prevpc(&site);
+            JS_ASSERT_IF(fp->prev(), !site);
         }
-        MarkStackRangeConservatively(trc, seg->slotsBegin(), slotsEnd);
+        gc::MarkRootRange(trc, seg->slotsBegin(), slotsEnd, "vm_stack");
         nextSegEnd = (Value *)seg;
     }
 }
 
 JS_FRIEND_API(bool)
 StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
                             JSCompartment *dest) const
 {
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=4 sw=4 et tw=79 ft=cpp:
  *
  * ***** 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
@@ -1194,16 +1194,19 @@ class StackFrame
 #ifdef JS_METHODJIT
     mjit::JITScript *jit() {
         return script()->getJIT(isConstructing());
     }
 #endif
 
     void methodjitStaticAsserts();
 
+  public:
+    void mark(JSTracer *trc);
+
     bool runningInIon() const {
         return !!(flags_ & RUNNING_IN_ION);
     }
     void setRunningInIon() {
         flags_ |= RUNNING_IN_ION;
     }
     void clearRunningInIon() {
         flags_ &= ~RUNNING_IN_ION;
@@ -1375,16 +1378,20 @@ class StackSegment
     StackFrame *fp() const {
         return regs_->fp();
     }
 
     StackFrame *maybefp() const {
         return regs_ ? regs_->fp() : NULL;
     }
 
+    jsbytecode *maybepc() const {
+        return regs_ ? regs_->pc : NULL;
+    }
+
     CallArgsList &calls() const {
         JS_ASSERT(calls_);
         return *calls_;
     }
 
     CallArgsList *maybeCalls() const {
         return calls_;
     }
@@ -1544,16 +1551,17 @@ class StackSpace
      * does indeed have this required space and reports an error and returns
      * NULL if this reserve space cannot be allocated.
      */
     inline Value *getStackLimit(JSContext *cx, MaybeReportError report);
     bool tryBumpLimit(JSContext *cx, Value *from, uintN nvals, Value **limit);
 
     /* Called during GC: mark segments, frames, and slots under firstUnused. */
     void mark(JSTracer *trc);
+    void markFrameSlots(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc);
 
     /* We only report the committed size;  uncommitted size is uninteresting. */
     JS_FRIEND_API(size_t) sizeOfCommitted();
 };
 
 /*****************************************************************************/
 
 class ContextStack
--- a/js/xpconnect/src/XPCInlines.h
+++ b/js/xpconnect/src/XPCInlines.h
@@ -577,31 +577,39 @@ inline void XPCNativeSet::ASSERT_NotMark
     for (int i = (int) mInterfaceCount; i > 0; i--, pp++)
         NS_ASSERTION(!(*pp)->IsMarked(), "bad");
 }
 #endif
 
 /***************************************************************************/
 
 inline
-JSObject* XPCWrappedNativeTearOff::GetJSObject() const
+JSObject* XPCWrappedNativeTearOff::GetJSObjectPreserveColor() const
 {
     return mJSObject;
 }
 
 inline
+JSObject* XPCWrappedNativeTearOff::GetJSObject()
+{
+    JSObject *obj = GetJSObjectPreserveColor();
+    xpc_UnmarkGrayObject(obj);
+    return obj;
+}
+
+inline
 void XPCWrappedNativeTearOff::SetJSObject(JSObject*  JSObj)
 {
         mJSObject = JSObj;
 }
 
 inline
 XPCWrappedNativeTearOff::~XPCWrappedNativeTearOff()
 {
-    NS_ASSERTION(!(GetInterface()||GetNative()||GetJSObject()), "tearoff not empty in dtor");
+    NS_ASSERTION(!(GetInterface()||GetNative()||GetJSObjectPreserveColor()), "tearoff not empty in dtor");
 }
 
 /***************************************************************************/
 
 inline JSBool
 XPCWrappedNative::HasInterfaceNoQI(const nsIID& iid)
 {
     return nsnull != GetSet()->FindInterfaceWithIID(iid);
@@ -616,17 +624,17 @@ XPCWrappedNative::SweepTearOffs()
         for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK; i > 0; i--, to++) {
             JSBool marked = to->IsMarked();
             to->Unmark();
             if (marked)
                 continue;
 
             // If this tearoff does not have a live dedicated JSObject,
             // then let's recycle it.
-            if (!to->GetJSObject()) {
+            if (!to->GetJSObjectPreserveColor()) {
                 nsISupports* obj = to->GetNative();
                 if (obj) {
                     obj->Release();
                     to->SetNative(nsnull);
                 }
                 to->SetInterface(nsnull);
             }
         }
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -166,17 +166,17 @@ XPCWrappedNative::NoteTearoffs(nsCycleCo
     // finalized yet we'll note the edge between the JS object and the native
     // (see nsXPConnect::Traverse), but if their JS object has been finalized
     // then the tearoff is only reachable through the XPCWrappedNative, so we
     // record an edge here.
     XPCWrappedNativeTearOffChunk* chunk;
     for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
         XPCWrappedNativeTearOff* to = chunk->mTearOffs;
         for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
-            JSObject* jso = to->GetJSObject();
+            JSObject* jso = to->GetJSObjectPreserveColor();
             if (!jso) {
                 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "tearoff's mNative");
                 cb.NoteXPCOMChild(to->GetNative());
             }
         }
     }
 }
 
@@ -1254,17 +1254,17 @@ XPCWrappedNative::FlatJSObjectFinalized(
     // This will keep them from trying to access their pointers to the
     // dying tearoff object. We can safely assume that those remaining
     // JSObjects are about to be finalized too.
 
     XPCWrappedNativeTearOffChunk* chunk;
     for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
         XPCWrappedNativeTearOff* to = chunk->mTearOffs;
         for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
-            JSObject* jso = to->GetJSObject();
+            JSObject* jso = to->GetJSObjectPreserveColor();
             if (jso) {
                 NS_ASSERTION(JS_IsAboutToBeFinalized(jso), "bad!");
                 JS_SetPrivate(jso, nsnull);
                 to->JSObjectFinalized();
             }
 
             // We also need to release any native pointers held...
             nsISupports* obj = to->GetNative();
@@ -1358,18 +1358,18 @@ XPCWrappedNative::SystemIsBeingShutDown(
     }
 
     // cleanup the tearoffs...
 
     XPCWrappedNativeTearOffChunk* chunk;
     for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
         XPCWrappedNativeTearOff* to = chunk->mTearOffs;
         for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
-            if (to->GetJSObject()) {
-                JS_SetPrivate(to->GetJSObject(), nsnull);
+            if (JSObject *jso = to->GetJSObjectPreserveColor()) {
+                JS_SetPrivate(jso, nsnull);
                 to->SetJSObject(nsnull);
             }
             // We leak the tearoff mNative
             // (for the same reason we leak mIdentity - see above).
             to->SetNative(nsnull);
             to->SetInterface(nsnull);
         }
     }
@@ -1772,17 +1772,17 @@ XPCWrappedNative::FindTearOff(XPCCallCon
          lastChunk = chunk, chunk = chunk->mNextChunk) {
         to = chunk->mTearOffs;
         XPCWrappedNativeTearOff* const end = chunk->mTearOffs +
             XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK;
         for (to = chunk->mTearOffs;
              to < end;
              to++) {
             if (to->GetInterface() == aInterface) {
-                if (needJSObject && !to->GetJSObject()) {
+                if (needJSObject && !to->GetJSObjectPreserveColor()) {
                     AutoMarkingWrappedNativeTearOffPtr tearoff(ccx, to);
                     JSBool ok = InitTearOffJSObject(ccx, to);
                     // During shutdown, we don't sweep tearoffs.  So make sure
                     // to unmark manually in case the auto-marker marked us.
                     // We shouldn't ever be getting here _during_ our
                     // Mark/Sweep cycle, so this should be safe.
                     to->Unmark();
                     if (!ok) {
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -322,16 +322,18 @@ XPCWrappedNativeScope::GetPrototypeNoHel
         mPrototypeNoHelper =
             xpc_NewSystemInheritingJSObject(ccx,
                                             js::Jsvalify(&XPC_WN_NoHelper_Proto_JSClass),
                                             mPrototypeJSObject,
                                             false, mGlobalJSObject);
 
         NS_ASSERTION(mPrototypeNoHelper,
                      "Failed to create prototype for wrappers w/o a helper");
+    } else {
+        xpc_UnmarkGrayObject(mPrototypeNoHelper);
     }
 
     return mPrototypeNoHelper;
 }
 
 static JSDHashOperator
 WrappedNativeJSGCThingTracer(JSDHashTable *table, JSDHashEntryHdr *hdr,
                              uint32_t number, void *arg)
--- a/js/xpconnect/src/dombindings.cpp
+++ b/js/xpconnect/src/dombindings.cpp
@@ -486,18 +486,20 @@ template<class LC>
 JSObject *
 ListBase<LC>::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope)
 {
     nsDataHashtable<nsDepCharHashKey, JSObject*> &cache =
         scope->GetCachedDOMPrototypes();
 
     JSObject *interfacePrototype;
     if (cache.IsInitialized()) {
-        if (cache.Get(sInterfaceClass.name, &interfacePrototype))
+        if (cache.Get(sInterfaceClass.name, &interfacePrototype)) {
+            xpc_UnmarkGrayObject(interfacePrototype);
             return interfacePrototype;
+        }
     } else if (!cache.Init()) {
         return NULL;
     }
 
     JSObject* proto = Base::getPrototype(cx, scope);
     if (!proto)
         return NULL;
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2377,17 +2377,18 @@ class XPCWrappedNativeTearOff
 public:
     JSBool IsAvailable() const {return mInterface == nsnull;}
     JSBool IsReserved()  const {return mInterface == (XPCNativeInterface*)1;}
     JSBool IsValid()     const {return !IsAvailable() && !IsReserved();}
     void   SetReserved()       {mInterface = (XPCNativeInterface*)1;}
 
     XPCNativeInterface* GetInterface() const {return mInterface;}
     nsISupports*        GetNative()    const {return mNative;}
-    JSObject*           GetJSObject()  const;
+    JSObject*           GetJSObject();
+    JSObject*           GetJSObjectPreserveColor() const;
     void SetInterface(XPCNativeInterface*  Interface) {mInterface = Interface;}
     void SetNative(nsISupports*  Native)              {mNative = Native;}
     void SetJSObject(JSObject*  JSObj);
 
     void JSObjectFinalized() {SetJSObject(nsnull);}
 
     XPCWrappedNativeTearOff()
         : mInterface(nsnull), mNative(nsnull), mJSObject(nsnull) {}
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -98,18 +98,20 @@ WrapperFactory::WaiveXray(JSContext *cx,
     // the .wrappedJSObject also wraps the outer window.
     obj = GetCurrentOuter(cx, obj);
 
     {
         // See if we already have a waiver wrapper for this object.
         CompartmentPrivate *priv =
             (CompartmentPrivate *)JS_GetCompartmentPrivate(cx, js::GetObjectCompartment(obj));
         JSObject *wobj = nsnull;
-        if (priv && priv->waiverWrapperMap)
+        if (priv && priv->waiverWrapperMap) {
             wobj = priv->waiverWrapperMap->Find(obj);
+            xpc_UnmarkGrayObject(wobj);
+        }
 
         // No wrapper yet, make one.
         if (!wobj) {
             JSObject *proto = js::GetObjectProto(obj);
             if (proto && !(proto = WaiveXray(cx, proto)))
                 return nsnull;
 
             JSAutoEnterCompartment ac;
--- a/layout/forms/test/test_bug36619.html
+++ b/layout/forms/test/test_bug36619.html
@@ -11,16 +11,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=36619">Mozilla Bug 36619</a>
 <p id="display"></p>
 <div id="content" style="display:none;">
   <input id='a' type='file'>
 </div>
 <button id='b' onclick="document.getElementById('a').click();">Show Filepicker</button>
+<button id='c' onmousedown="document.getElementById('a').click();">Show Filepicker</button>
+<button id='d' onmouseup="document.getElementById('a').click();">Show Filepicker</button>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 36619 **/
 
 SimpleTest.waitForExplicitFinish();
 
 var MockFilePicker = SpecialPowers.MockFilePicker;
@@ -31,28 +33,40 @@ SpecialPowers.pushPrefEnv({'set': [
   ["dom.disable_open_during_load", true],
   ["privacy.popups.showBrowserMessage", false]
 ]}, function() {
   SimpleTest.waitForFocus(function() {
 
     // Tests that a click on 'b' calls the show method.
     var b = document.getElementById('b');
     b.focus(); // Be sure the element is visible.
-    synthesizeMouse(b, 2, 2, {});
+    synthesizeMouseAtCenter(b, {});
     SimpleTest.executeSoon(function() {
       ok(MockFilePicker.shown, "File picker show method should have been called");
       MockFilePicker.reset();
 
       // Tests that a click on 'a' doesn't call the show method, because it is hidden.
       document.getElementById("a").click();
       SimpleTest.executeSoon(function() {
         ok(!MockFilePicker.shown, "File picker show method should not have been called");
-        MockFilePicker.cleanup();
+        MockFilePicker.reset();
+
+        synthesizeMouseAtCenter(document.getElementById('c'), {});
+        SimpleTest.executeSoon(function() {
+          ok(!MockFilePicker.shown, "File picker show method should have been called");
+          MockFilePicker.reset();
 
-        SimpleTest.finish();
+          synthesizeMouseAtCenter(document.getElementById('d'), {});
+          SimpleTest.executeSoon(function() {
+            ok(MockFilePicker.shown, "File picker show method should have been called");
+
+            MockFilePicker.cleanup();
+            SimpleTest.finish();
+          });
+        });
       });
     });
   });
 });
 
 </script>
 </pre>
 </body>
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -1231,16 +1231,17 @@ nsCSSValue::SizeOfExcludingThis(nsMalloc
     case eCSSUnit_Point:
     case eCSSUnit_Inch:
     case eCSSUnit_Millimeter:
     case eCSSUnit_Centimeter:
     case eCSSUnit_Pica:
     case eCSSUnit_Pixel:
     case eCSSUnit_Degree:
     case eCSSUnit_Grad:
+    case eCSSUnit_Turn:
     case eCSSUnit_Radian:
     case eCSSUnit_Hertz:
     case eCSSUnit_Kilohertz:
     case eCSSUnit_Seconds:
     case eCSSUnit_Milliseconds:
       break;
 
     default:
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -2,12 +2,13 @@
 [testBookmark]
 [testLoad]
 [testNewTab]
 [testPanCorrectness]
 [test_bug720538]
 [testFlingCorrectness]
 [testOverscroll]
 [testAxisLocking]
+[testAboutPage]
 
 # Used for Talos, please don't use in mochitest
 #[testPan]
 #[testCheck]
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testAboutPage.java.in
@@ -0,0 +1,47 @@
+#filter substitution
+package @ANDROID_PACKAGE_NAME@.tests;
+
+import @ANDROID_PACKAGE_NAME@.*;
+import android.app.Activity;
+import android.util.Log;
+
+public class testAboutPage extends BaseTest {
+    public void testAboutPage() {
+        setTestType("mochitest");
+        mActions.expectGeckoEvent("Gecko:Ready").blockForEvent();
+
+        // Load the about: page
+        String url = "about:";
+        loadUrl(url);
+
+        Element awesomebar = mDriver.findElement(getActivity(), "awesome_bar");
+        mAsserter.ok(awesomebar.getText().matches("About (Fennec|Nightly|Aurora|Firefox)"), "page title match", "about: page title is correct");
+
+        // Open a new page to remove the about: page from the current tab
+        url = getAbsoluteUrl("/robocop/robocop_blank_01.html");
+        loadUrl(url);
+
+        // Use the menu to open the Settings
+        mActions.sendSpecialKey(Actions.SpecialKey.MENU);
+
+        // Look for the 'More' menu if this device/OS uses it
+        if (mSolo.waitForText("^More$")) {
+            mSolo.clickOnText("^More$");
+        }
+
+        mSolo.waitForText("^Settings$");
+        mSolo.clickOnText("^Settings$");
+
+        // Tap on the "About Xxxx" setting
+        mSolo.waitForText("About (Fennec|Nightly|Aurora|Firefox)");
+        mSolo.clickOnText("About (Fennec|Nightly|Aurora|Firefox)");
+
+        // Wait for the new tab and page to load
+        mActions.expectGeckoEvent("Tab:Added").blockForEvent();
+        mActions.expectGeckoEvent("DOMContentLoaded").blockForEvent();
+
+        // Grab the title to make sure the about: page was loaded
+        awesomebar = mDriver.findElement(getActivity(), "awesome_bar");
+        mAsserter.ok(awesomebar.getText().matches("About (Fennec|Nightly|Aurora|Firefox)"), "page title match", "about: page title is correct");
+    }
+}
--- a/mobile/android/base/tests/test_bug720538.java.in
+++ b/mobile/android/base/tests/test_bug720538.java.in
@@ -47,11 +47,16 @@ public class test_bug720538 extends Pixe
         // now we do double-tap again to zoom out and wait for the animation to finish, as before
         paintExpecter = mActions.expectPaint();
         meh.doubleTap(100, mDriver.getGeckoWidth() / 2);
         painted = waitForPaint(paintExpecter);
 
         // and now we check a pixel at the bottom of the view to ensure that we have the page
         // background and not some checkerboarding. use the second-last row of pixels instead of
         // the last row because the last row is subject to rounding and clipping errors
+        for (int y = 2; y < 10; y++) {
+            for (int x = 0; x < 10; x++) {
+                mAsserter.dumpLog("Pixel at " + x + ", " + (mDriver.getGeckoHeight() - y) + ": " + Integer.toHexString(painted[mDriver.getGeckoHeight() - y][x]));
+            }
+        }
         mAsserter.ispixel(painted[mDriver.getGeckoHeight() - 2][0], 0, 0x80, 0, "Checking bottom-left corner of viewport");
     }
 }
--- a/toolkit/components/maintenanceservice/workmonitor.cpp
+++ b/toolkit/components/maintenanceservice/workmonitor.cpp
@@ -411,17 +411,17 @@ ProcessSoftwareUpdateCommand(DWORD argc,
     // When there is a certificate check error on the updater.exe application,
     // we want to write out the error.
     if (!WriteStatusFailure(argv[1], 
                             SERVICE_UPDATER_SIGN_ERROR)) {
       LOG(("Could not write pending state to update.status.  (%d)\n", 
            GetLastError()));
     }
   }
-  LocalFree(argv);
+
   return result;
 }
 
 /**
  * Executes a service command.
  *
  * @param argc The number of arguments in argv
  * @param argv The service command line arguments, argv[0] and argv[1]
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js
@@ -10,18 +10,18 @@
 
 do_load_httpd_js();
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
 
 const PATH = "/submit/telemetry/test-ping";
 const SERVER = "http://localhost:4444";
 const IGNORE_HISTOGRAM = "test::ignore_me";
-const IGNORE_HISTOGRAM_TO_CLONE = "MEMORY_HEAP_ALLOCATED"
-const IGNORE_CLONED_HISTOGRAM = "test::ignore_me_also"
+const IGNORE_HISTOGRAM_TO_CLONE = "MEMORY_HEAP_ALLOCATED";
+const IGNORE_CLONED_HISTOGRAM = "test::ignore_me_also";
 
 const BinaryInputStream = Components.Constructor(
   "@mozilla.org/binaryinputstream;1",
   "nsIBinaryInputStream",
   "setInputStream");
 const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
 
 var httpserver = new nsHttpServer();
@@ -56,39 +56,46 @@ function telemetryObserver(aSubject, aTo
 function checkHistograms(request, response) {
   // do not need the http server anymore
   httpserver.stop(do_test_finished);
   let s = request.bodyInputStream;
   let payload = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON)
                                              .decodeFromStream(s, s.available());
 
   do_check_eq(request.getHeader("content-type"), "application/json; charset=UTF-8");
-  do_check_true(payload.simpleMeasurements.uptime >= 0)
+  do_check_true(payload.simpleMeasurements.uptime >= 0);
   do_check_true(payload.simpleMeasurements.startupInterrupted === 1);
   // get rid of the non-deterministic field
   const expected_info = {
     reason: "test-ping",
     OS: "XPCShell", 
     appID: "xpcshell@tests.mozilla.org", 
     appVersion: "1", 
     appName: "XPCShell", 
     appBuildID: "2007010101",
     platformBuildID: "2007010101"
   };
 
   for (let f in expected_info) {
     do_check_eq(payload.info[f], expected_info[f]);
   }
 
-  var isWindows = ("@mozilla.org/windows-registry-key;1" in Components.classes);
-  var isOSX = ("nsILocalFileMac" in Components.interfaces);
+  try {
+    // If we've not got nsIGfxInfoDebug, then this will throw and stop us doing
+    // this test.
+    var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
+    var isWindows = ("@mozilla.org/windows-registry-key;1" in Components.classes);
+    var isOSX = ("nsILocalFileMac" in Components.interfaces);
 
-  if (isWindows || isOSX) {
-    do_check_true("adapterVendorID" in payload.info);
-    do_check_true("adapterDeviceID" in payload.info);
+    if (isWindows || isOSX) {
+      do_check_true("adapterVendorID" in payload.info);
+      do_check_true("adapterDeviceID" in payload.info);
+    }
+  }
+  catch (x) {
   }
 
   const TELEMETRY_PING = "TELEMETRY_PING";
   const TELEMETRY_SUCCESS = "TELEMETRY_SUCCESS";
   do_check_true(TELEMETRY_PING in payload.histograms);
   let rh = Telemetry.registeredHistograms;
   for (let name in rh) {
     if (/SQLITE/.test(name) && name in payload.histograms) {
@@ -100,18 +107,18 @@ function checkHistograms(request, respon
 
   // There should be one successful report from the previous telemetry ping.
   const expected_tc = {
     range: [1, 2],
     bucket_count: 3,
     histogram_type: 2,
     values: {0:1, 1:1, 2:0},
     sum: 1
-  }
-  let tc = payload.histograms[TELEMETRY_SUCCESS]
+  };
+  let tc = payload.histograms[TELEMETRY_SUCCESS];
   do_check_eq(uneval(tc), 
               uneval(expected_tc));
 
   do_check_true(("mainThread" in payload.slowSQL) &&
                 ("otherThreads" in payload.slowSQL));
   gFinished = true;
 }
 
@@ -171,26 +178,34 @@ function dummyTheme(id) {
     headerURL: "http://lwttest.invalid/a.png",
     footerURL: "http://lwttest.invalid/b.png",
     textcolor: Math.random().toString(),
     accentcolor: Math.random().toString()
   };
 }
 
 function run_test() {
+  try {
+    var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
+    gfxInfo.spoofVendorID("0xabcd");
+    gfxInfo.spoofDeviceID("0x1234");
+  } catch (x) {
+    // If we can't test gfxInfo, that's fine, we'll note it later.
+  }
+
   // Addon manager needs a profile directory
   do_get_profile();
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
   // try to make LightweightThemeManager do stuff
   let gInternalManager = Cc["@mozilla.org/addons/integration;1"]
                          .getService(Ci.nsIObserver)
                          .QueryInterface(Ci.nsITimerCallback);
 
   gInternalManager.observe(null, "addons-startup", null);
   LightweightThemeManager.currentTheme = dummyTheme("1234");
 
   Services.obs.addObserver(nonexistentServerObserver, "telemetry-test-xhr-complete", false);
   telemetry_ping();
   // spin the event loop
   do_test_pending();
   // ensure that test runs to completion
-  do_register_cleanup(function () do_check_true(gFinished))
+  do_register_cleanup(function () do_check_true(gFinished));
  }
--- a/toolkit/devtools/debugger/server/dbg-script-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-script-actors.js
@@ -1090,16 +1090,20 @@ FrameActor.prototype = {
     }
 
     let envActor = this.threadActor
                        .createEnvironmentActor(this.frame,
                                                this.frameLifetimePool);
     grip.environment = envActor ? envActor.grip() : envActor;
     grip["this"] = this.threadActor.createValueGrip(this.frame["this"]);
     grip.arguments = this._args();
+    if (this.frame.script) {
+      grip.where = { url: this.frame.script.url,
+                     line: this.frame.script.getOffsetLine(this.frame.offset) };
+    }
 
     if (!this.frame.older) {
       grip.oldest = true;
     }
 
     return grip;
   },
 
--- a/tools/profiler/Makefile.in
+++ b/tools/profiler/Makefile.in
@@ -80,26 +80,22 @@ DEFINES += -DMOZ_ENABLE_PROFILER_SPS
 
 CPPSRCS += \
   shared-libraries-linux.cc \
   platform-linux.cc \
   TableTicker.cpp \
   $(NULL)
 endif
 ifeq ($(OS_TARGET),Darwin)
-# For now we use platform-linux.cc because we can't unwind
-# another thread on mac using backtrace(), the implementation
-# for platform-macosx.cc is in the hg history and should be
-# used when we can stackwalk using a thread handle.
 
 DEFINES += -DMOZ_ENABLE_PROFILER_SPS
 
 CPPSRCS += \
   shared-libraries-macos.cc \
-  platform-linux.cc \
+  platform-macos.cc \
   TableTicker.cpp \
   $(NULL)
 endif
 
 ifeq ($(OS_TARGET),WINNT)
 
 DEFINES += -DMOZ_ENABLE_PROFILER_SPS
 
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -40,28 +40,29 @@
 #include <stdio.h>
 #include "sps_sampler.h"
 #include "platform.h"
 #include "nsXULAppAPI.h"
 #include "nsThreadUtils.h"
 #include "prenv.h"
 #include "shared-libraries.h"
 #include "mozilla/StringBuilder.h"
+#include "mozilla/StackWalk.h"
 
 // we eventually want to make this runtime switchable
-#if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_UNIX))
+#if defined(MOZ_PROFILING) && (defined(XP_UNIX) && !defined(XP_MACOSX))
  #ifndef ANDROID
   #define USE_BACKTRACE
  #endif
 #endif
 #ifdef USE_BACKTRACE
  #include <execinfo.h>
 #endif
 
-#if defined(MOZ_PROFILING) && defined(XP_WIN)
+#if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN))
  #define USE_NS_STACKWALK
 #endif
 #ifdef USE_NS_STACKWALK
  #include "nsStackWalk.h"
 #endif
 
 using std::string;
 using namespace mozilla;
@@ -348,17 +349,17 @@ class TableTicker: public Sampler {
 
   Profile* GetProfile()
   {
     return &mProfile;
   }
 
 private:
   // Not implemented on platforms which do not support backtracing
-  void doBacktrace(Profile &aProfile);
+  void doBacktrace(Profile &aProfile, Address pc);
 
 private:
   Profile mProfile;
   Stack *mStack;
   bool mSaveRequested;
   bool mUseStackWalk;
   bool mJankOnly;
 };
@@ -414,31 +415,33 @@ void TableTicker::HandleSaveRequest()
   mSaveRequested = false;
 
   // TODO: Use use the ipc/chromium Tasks here to support processes
   // without XPCOM.
   nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
   NS_DispatchToMainThread(runnable);
 }
 
+
 #ifdef USE_BACKTRACE
-void TableTicker::doBacktrace(Profile &aProfile)
+void TableTicker::doBacktrace(Profile &aProfile, Address pc)
 {
   void *array[100];
   int count = backtrace (array, 100);
 
   aProfile.addTag(ProfileEntry('s', "(root)", 0));
 
   for (int i = 0; i < count; i++) {
     if( (intptr_t)array[i] == -1 ) break;
     aProfile.addTag(ProfileEntry('l', (const char*)array[i]));
   }
 }
 #endif
 
+
 #ifdef USE_NS_STACKWALK
 typedef struct {
   void** array;
   size_t size;
   size_t count;
 } PCArray;
 
 static
@@ -447,27 +450,33 @@ void StackWalkCallback(void* aPC, void* 
   PCArray* array = static_cast<PCArray*>(aClosure);
   if (array->count >= array->size) {
     // too many frames, ignore
     return;
   }
   array->array[array->count++] = aPC;
 }
 
-void TableTicker::doBacktrace(Profile &aProfile)
+void TableTicker::doBacktrace(Profile &aProfile, Address fp)
 {
+#ifndef XP_MACOSX
   uintptr_t thread = GetThreadHandle(platform_data());
   MOZ_ASSERT(thread);
+#endif
   void* pc_array[1000];
   PCArray array = {
     pc_array,
     mozilla::ArrayLength(pc_array),
     0
   };
+#ifdef XP_MACOSX
+  nsresult rv = FramePointerStackWalk(StackWalkCallback, 1, &array, reinterpret_cast<void**>(fp));
+#else
   nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread);
+#endif
   if (NS_SUCCEEDED(rv)) {
     aProfile.addTag(ProfileEntry('s', "(root)", 0));
 
     for (size_t i = array.count; i > 0; --i) {
       aProfile.addTag(ProfileEntry('l', (const char*)array.array[i - 1]));
     }
   }
 }
@@ -534,17 +543,17 @@ void TableTicker::Tick(TickSample* sampl
       if (delta.ToMilliseconds() > 100.0) {
           recordSample = true;
       }
     }
   }
 
 #if defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK)
   if (mUseStackWalk) {
-    doBacktrace(mProfile);
+    doBacktrace(mProfile, sample->fp);
   } else {
     doSampleStackTrace(mStack, mProfile, sample);
   }
 #else
   doSampleStackTrace(mStack, mProfile, sample);
 #endif
 
   if (recordSample)
new file mode 100644
--- /dev/null
+++ b/tools/profiler/platform-macos.cc
@@ -0,0 +1,309 @@
+#include <dlfcn.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <mach/mach_init.h>
+#include <mach-o/dyld.h>
+#include <mach-o/getsect.h>
+
+#include <AvailabilityMacros.h>
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <libkern/OSAtomic.h>
+#include <mach/mach.h>
+#include <mach/semaphore.h>
+#include <mach/task.h>
+#include <mach/vm_statistics.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+
+#include "platform.h"
+
+// this port is based off of v8 svn revision 9837
+
+// XXX: this is a very stubbed out implementation
+// that only supports a single Sampler
+struct SamplerRegistry {
+  static void AddActiveSampler(Sampler *sampler) {
+    ASSERT(!SamplerRegistry::sampler);
+    SamplerRegistry::sampler = sampler;
+  }
+  static void RemoveActiveSampler(Sampler *sampler) {
+    SamplerRegistry::sampler = NULL;
+  }
+  static Sampler *sampler;
+};
+
+Sampler *SamplerRegistry::sampler = NULL;
+
+// 0 is never a valid thread id on MacOSX since a ptread_t is
+// a pointer.
+static const pthread_t kNoThread = (pthread_t) 0;
+
+class MacOSMutex : public Mutex {
+ public:
+  MacOSMutex() {
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+    pthread_mutex_init(&mutex_, &attr);
+  }
+
+  virtual ~MacOSMutex() { pthread_mutex_destroy(&mutex_); }
+
+  virtual int Lock() { return pthread_mutex_lock(&mutex_); }
+  virtual int Unlock() { return pthread_mutex_unlock(&mutex_); }
+
+  virtual bool TryLock() {
+    int result = pthread_mutex_trylock(&mutex_);
+    // Return false if the lock is busy and locking failed.
+    if (result == EBUSY) {
+      return false;
+    }
+    ASSERT(result == 0);  // Verify no other errors.
+    return true;
+  }
+
+ private:
+  pthread_mutex_t mutex_;
+};
+
+
+Mutex* OS::CreateMutex() {
+  return new MacOSMutex();
+}
+
+void OS::Sleep(int milliseconds) {
+  usleep(1000 * milliseconds);
+}
+
+class Thread::PlatformData : public Malloced {
+ public:
+  PlatformData() : thread_(kNoThread) {}
+  pthread_t thread_;  // Thread handle for pthread.
+};
+
+Thread::Thread(const char* name)
+    : data_(new PlatformData),
+      stack_size_(0) {
+  set_name(name);
+}
+
+
+Thread::~Thread() {
+  delete data_;
+}
+
+
+static void SetThreadName(const char* name) {
+  // pthread_setname_np is only available in 10.6 or later, so test
+  // for it at runtime.
+  int (*dynamic_pthread_setname_np)(const char*);
+  *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
+    dlsym(RTLD_DEFAULT, "pthread_setname_np");
+  if (!dynamic_pthread_setname_np)
+    return;
+
+  // Mac OS X does not expose the length limit of the name, so hardcode it.
+  static const int kMaxNameLength = 63;
+  USE(kMaxNameLength);
+  ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
+  dynamic_pthread_setname_np(name);
+}
+
+
+static void* ThreadEntry(void* arg) {
+  Thread* thread = reinterpret_cast<Thread*>(arg);
+  // This is also initialized by the first argument to pthread_create() but we
+  // don't know which thread will run first (the original thread or the new
+  // one) so we initialize it here too.
+  thread->data()->thread_ = pthread_self();
+  SetThreadName(thread->name());
+  ASSERT(thread->data()->thread_ != kNoThread);
+  thread->Run();
+  return NULL;
+}
+
+
+void Thread::set_name(const char* name) {
+  strncpy(name_, name, sizeof(name_));
+  name_[sizeof(name_) - 1] = '\0';
+}
+
+
+void Thread::Start() {
+  pthread_attr_t* attr_ptr = NULL;
+  pthread_attr_t attr;
+  if (stack_size_ > 0) {
+    pthread_attr_init(&attr);
+    pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
+    attr_ptr = &attr;
+  }
+  pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
+  ASSERT(data_->thread_ != kNoThread);
+}
+
+void Thread::Join() {
+  pthread_join(data_->thread_, NULL);
+}
+
+class Sampler::PlatformData : public Malloced {
+ public:
+  PlatformData() : profiled_thread_(mach_thread_self()) {}
+
+  ~PlatformData() {
+    // Deallocate Mach port for thread.
+    mach_port_deallocate(mach_task_self(), profiled_thread_);
+  }
+
+  thread_act_t profiled_thread() { return profiled_thread_; }
+
+ private:
+  // Note: for profiled_thread_ Mach primitives are used instead of PThread's
+  // because the latter doesn't provide thread manipulation primitives required.
+  // For details, consult "Mac OS X Internals" book, Section 7.3.
+  thread_act_t profiled_thread_;
+};
+
+
+class SamplerThread : public Thread {
+ public:
+  explicit SamplerThread(int interval)
+      : Thread("SamplerThread"),
+        interval_(interval) {}
+
+  static void AddActiveSampler(Sampler* sampler) {
+    ScopedLock lock(mutex_);
+    SamplerRegistry::AddActiveSampler(sampler);
+    if (instance_ == NULL) {
+      instance_ = new SamplerThread(sampler->interval());
+      instance_->Start();
+    } else {
+      ASSERT(instance_->interval_ == sampler->interval());
+    }
+  }
+
+  static void RemoveActiveSampler(Sampler* sampler) {
+    ScopedLock lock(mutex_);
+    instance_->Join();
+    //XXX: unlike v8 we need to remove the active sampler after doing the Join
+    // because we drop the sampler immediately
+    SamplerRegistry::RemoveActiveSampler(sampler);
+    delete instance_;
+    instance_ = NULL;
+    /*
+    if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
+      RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
+      delete instance_;
+      instance_ = NULL;
+    }
+    */
+  }
+
+  // Implement Thread::Run().
+  virtual void Run() {
+    while (SamplerRegistry::sampler->IsActive()) {
+      SampleContext(SamplerRegistry::sampler);
+      OS::Sleep(interval_);
+    }
+  }
+
+  void SampleContext(Sampler* sampler) {
+    thread_act_t profiled_thread = sampler->platform_data()->profiled_thread();
+    TickSample sample_obj;
+    TickSample* sample = &sample_obj;
+    //TickSample* sample = CpuProfiler::TickSampleEvent(sampler->isolate());
+    //if (sample == NULL) sample = &sample_obj;
+
+    if (KERN_SUCCESS != thread_suspend(profiled_thread)) return;
+
+#if V8_HOST_ARCH_X64
+    thread_state_flavor_t flavor = x86_THREAD_STATE64;
+    x86_thread_state64_t state;
+    mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
+#if __DARWIN_UNIX03
+#define REGISTER_FIELD(name) __r ## name
+#else
+#define REGISTER_FIELD(name) r ## name
+#endif  // __DARWIN_UNIX03
+#elif V8_HOST_ARCH_IA32
+    thread_state_flavor_t flavor = i386_THREAD_STATE;
+    i386_thread_state_t state;
+    mach_msg_type_number_t count = i386_THREAD_STATE_COUNT;
+#if __DARWIN_UNIX03
+#define REGISTER_FIELD(name) __e ## name
+#else
+#define REGISTER_FIELD(name) e ## name
+#endif  // __DARWIN_UNIX03
+#else
+#error Unsupported Mac OS X host architecture.
+#endif  // V8_HOST_ARCH
+
+    if (thread_get_state(profiled_thread,
+                         flavor,
+                         reinterpret_cast<natural_t*>(&state),
+                         &count) == KERN_SUCCESS) {
+      //sample->state = sampler->isolate()->current_vm_state();
+      sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
+      sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
+      sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
+      sample->timestamp = mozilla::TimeStamp::Now();
+      sampler->SampleStack(sample);
+      sampler->Tick(sample);
+    }
+    thread_resume(profiled_thread);
+  }
+
+  const int interval_;
+  //RuntimeProfilerRateLimiter rate_limiter_;
+
+  // Protects the process wide state below.
+  static Mutex* mutex_;
+  static SamplerThread* instance_;
+
+  DISALLOW_COPY_AND_ASSIGN(SamplerThread);
+};
+
+#undef REGISTER_FIELD
+
+
+Mutex* SamplerThread::mutex_ = OS::CreateMutex();
+SamplerThread* SamplerThread::instance_ = NULL;
+
+
+Sampler::Sampler(int interval, bool profiling)
+    : // isolate_(isolate),
+      interval_(interval),
+      profiling_(profiling),
+      active_(false) /*,
+      samples_taken_(0)*/ {
+  data_ = new PlatformData;
+}
+
+
+Sampler::~Sampler() {
+  ASSERT(!IsActive());
+  delete data_;
+}
+
+
+void Sampler::Start() {
+  ASSERT(!IsActive());
+  SetActive(true);
+  SamplerThread::AddActiveSampler(this);
+}
+
+
+void Sampler::Stop() {
+  ASSERT(IsActive());
+  SetActive(false);
+  SamplerThread::RemoveActiveSampler(this);
+}
--- a/widget/xpwidgets/PuppetWidget.cpp
+++ b/widget/xpwidgets/PuppetWidget.cpp
@@ -525,16 +525,18 @@ PuppetWidget::DispatchPaintEvent()
     debug_DumpPaintEvent(stderr, this, &event,
                          nsCAutoString("PuppetWidget"), nsnull);
 #endif
 
     if (LayerManager::LAYERS_D3D10 == mLayerManager->GetBackendType()) {
       DispatchEvent(&event, status);
     } else {
       nsRefPtr<gfxContext> ctx = new gfxContext(mSurface);
+      ctx->Rectangle(gfxRect(0,0,0,0));
+      ctx->Clip();
       AutoLayerManagerSetup setupLayerManager(this, ctx,
                                               BasicLayerManager::BUFFER_NONE);
       DispatchEvent(&event, status);  
     }
   }
 
   nsPaintEvent didPaintEvent(true, NS_DID_PAINT, this);
   DispatchEvent(&didPaintEvent, status);
--- a/xpcom/base/Makefile.in
+++ b/xpcom/base/Makefile.in
@@ -98,16 +98,17 @@ EXPORTS		= \
 
 EXPORTS_NAMESPACES = mozilla
 
 EXPORTS_mozilla = \
 	FunctionTimer.h \
 	MapsMemoryReporter.h \
 	ClearOnShutdown.h \
 	AvailableMemoryTracker.h \
+	StackWalk.h \
 	$(NULL)
 
 ifeq (windows,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += nsCrashOnException.cpp
 endif
 
 ifeq ($(OS_ARCH),WINNT)
 
new file mode 100644
--- /dev/null
+++ b/xpcom/base/StackWalk.h
@@ -0,0 +1,52 @@
+/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
+/* ***** 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 Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jeff Muizelaar <jmuizelaar@mozilla.com>
+ *
+ * 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 ***** */
+
+/* API for getting a stack trace of the C/C++ */
+
+#ifndef StackWalk_h_
+#define StackWalk_h_
+
+// XXX: it would be nice to eventually remove this header dependency on nsStackWalk.h
+#include "nsStackWalk.h"
+
+namespace mozilla {
+
+nsresult
+FramePointerStackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
+                      void *aClosure, void **bp);
+
+}
+
+#endif /* !defined(StackWalk_h_) */
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -136,20 +136,18 @@
 #include "nsBaseHashtable.h"
 #include "nsHashKeys.h"
 #include "nsDeque.h"
 #include "nsCycleCollector.h"
 #include "nsThreadUtils.h"
 #include "prenv.h"
 #include "prprf.h"
 #include "plstr.h"
-#include "prtime.h"
 #include "nsPrintfCString.h"
 #include "nsTArray.h"
-#include "mozilla/FunctionTimer.h"
 #include "nsIObserverService.h"
 #include "nsIConsoleService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "nsTArray.h"
 #include "mozilla/Services.h"
 #include "nsICycleCollectorListener.h"
 #include "nsIXPConnect.h"
@@ -307,16 +305,46 @@ struct nsCycleCollectorStats
 };
 #endif
 
 #ifdef DEBUG_CC
 static bool nsCycleCollector_shouldSuppress(nsISupports *s);
 static void InitMemHook(void);
 #endif
 
+#ifdef COLLECT_TIME_DEBUG
+class TimeLog
+{
+public:
+    TimeLog() : mLastCheckpoint(TimeStamp::Now()) {}
+
+    void
+    Checkpoint(const char* aEvent)
+    {
+        TimeStamp now = TimeStamp::Now();
+        PRUint32 dur = (PRUint32) ((now - mLastCheckpoint).ToMilliseconds());
+        if (dur > 0) {
+            printf("cc: %s took %dms\n", aEvent, dur);
+        }
+        mLastCheckpoint = now;
+    }
+
+private:
+    TimeStamp mLastCheckpoint;
+};
+#else
+class TimeLog
+{
+public:
+    TimeLog() {}
+    void Checkpoint(const char* aEvent) {}
+};
+#endif
+
+
 ////////////////////////////////////////////////////////////////////////
 // Base types
 ////////////////////////////////////////////////////////////////////////
 
 struct PtrInfo;
 
 class EdgePool
 {
@@ -2192,16 +2220,17 @@ nsCycleCollector::CollectWhite(nsICycleC
     // operating on are stable for the duration of our operation. So we
     // make 3 sets of calls to language runtimes:
     //
     //   - Root(whites), which should pin the whites in memory.
     //   - Unlink(whites), which drops outgoing links on each white.
     //   - Unroot(whites), which returns the whites to normal GC.
 
     nsresult rv;
+    TimeLog timeLog;
 
     NS_ASSERTION(mWhiteNodes->IsEmpty(),
                  "FinishCollection wasn't called?");
 
     mWhiteNodes->SetCapacity(mWhiteNodeCount);
 
     NodePool::Enumerator etor(mGraph.mNodes);
     while (!etor.IsDone())
@@ -2210,19 +2239,21 @@ nsCycleCollector::CollectWhite(nsICycleC
         if (pinfo->mColor == white && mWhiteNodes->AppendElement(pinfo)) {
             rv = pinfo->mParticipant->Root(pinfo->mPointer);
             if (NS_FAILED(rv)) {
                 Fault("Failed root call while unlinking", pinfo);
                 mWhiteNodes->RemoveElementAt(mWhiteNodes->Length() - 1);
             }
         }
     }
+    timeLog.Checkpoint("CollectWhite::Root");
 
     if (mBeforeUnlinkCB) {
         mBeforeUnlinkCB();
+        timeLog.Checkpoint("CollectWhite::BeforeUnlinkCB");
     }
 #if defined(DEBUG_CC) && !defined(__MINGW32__) && defined(WIN32)
     struct _CrtMemState ms1, ms2;
     _CrtMemCheckpoint(&ms1);
 #endif
 
     PRUint32 i, count = mWhiteNodes->Length();
 
@@ -2244,23 +2275,25 @@ nsCycleCollector::CollectWhite(nsICycleC
 #endif
         }
         else {
 #ifdef DEBUG_CC
             ++mStats.mCollectedNode;
 #endif
         }
     }
+    timeLog.Checkpoint("CollectWhite::Unlink");
 
     for (i = 0; i < count; ++i) {
         PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
         rv = pinfo->mParticipant->Unroot(pinfo->mPointer);
         if (NS_FAILED(rv))
             Fault("Failed unroot call while unlinking", pinfo);
     }
+    timeLog.Checkpoint("CollectWhite::Unroot");
 
 #if defined(DEBUG_CC) && !defined(__MINGW32__) && defined(WIN32)
     _CrtMemCheckpoint(&ms2);
     if (ms2.lTotalCount < ms1.lTotalCount)
         mStats.mFreedBytes += (ms1.lTotalCount - ms2.lTotalCount);
 #endif
 
     mCollectedObjects += count;
@@ -2837,80 +2870,79 @@ nsCycleCollector::GCIfNeeded(bool aForce
     if (!aForceGC) {
         bool needGC = rt->NeedCollect();
         // Only do a telemetry ping for non-shutdown CCs.
         Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_NEED_GC, needGC);
         if (!needGC)
             return;
     }
 
-#ifdef COLLECT_TIME_DEBUG
-    PRTime start = PR_Now();
-#endif
+    TimeLog timeLog;
+
     // rt->Collect() must be called from the main thread,
     // because it invokes XPCJSRuntime::GCCallback(cx, JSGC_BEGIN)
     // which returns false if not in the main thread.
     rt->Collect(js::gcreason::CC_FORCED, nsGCNormal);
-#ifdef COLLECT_TIME_DEBUG
-    printf("cc: GC() took %lldms\n", (PR_Now() - start) / PR_USEC_PER_MSEC);
-#endif
+    timeLog.Checkpoint("GC()");
 }
 
 bool
 nsCycleCollector::PrepareForCollection(nsTArray<PtrInfo*> *aWhiteNodes)
 {
 #if defined(DEBUG_CC) && !defined(__MINGW32__)
     if (!mParams.mDoNothing && mParams.mHookMalloc)
         InitMemHook();
 #endif
 
     // This can legitimately happen in a few cases. See bug 383651.
     if (mCollectionInProgress)
         return false;
 
-    NS_TIME_FUNCTION;
-
-#ifdef COLLECT_TIME_DEBUG
-    printf("cc: nsCycleCollector::PrepareForCollection()\n");
-#endif
+    TimeLog timeLog;
+
     mCollectionStart = TimeStamp::Now();
     mVisitedRefCounted = 0;
     mVisitedGCed = 0;
 
     mCollectionInProgress = true;
 
     nsCOMPtr<nsIObserverService> obs =
         mozilla::services::GetObserverService();
     if (obs)
         obs->NotifyObservers(nsnull, "cycle-collector-begin", nsnull);
 
     mFollowupCollection = false;
     mCollectedObjects = 0;
 
     mWhiteNodes = aWhiteNodes;
 
+    timeLog.Checkpoint("PrepareForCollection()");
+
     return true;
 }
 
 void
 nsCycleCollector::CleanupAfterCollection()
 {
     mWhiteNodes = nsnull;
     mCollectionInProgress = false;
 
 #ifdef XP_OS2
     // Now that the cycle collector has freed some memory, we can try to
     // force the C library to give back as much memory to the system as
     // possible.
     _heapmin();
 #endif
 
-    PRUint32 interval((TimeStamp::Now() - mCollectionStart).ToMilliseconds());
+    PRUint32 interval = (PRUint32) ((TimeStamp::Now() - mCollectionStart).ToMilliseconds());
 #ifdef COLLECT_TIME_DEBUG
-    printf("cc: CleanupAfterCollection(), total time %ums\n", interval);
+    printf("cc: total cycle collector time was %ums\n", interval);
+    printf("cc: visited %u ref counted and %u GCed objects, freed %d.\n",
+           mVisitedRefCounted, mVisitedGCed, mWhiteNodeCount);
+    printf("cc: \n");
 #endif
     Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR, interval);
     Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_REF_COUNTED, mVisitedRefCounted);
     Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_GCED, mVisitedGCed);
     Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_COLLECTED, mWhiteNodeCount);
 
 #ifdef DEBUG_CC
     ExplainLiveExpectedGarbage();
@@ -2943,37 +2975,31 @@ nsCycleCollector::Collect(PRUint32 aTryC
 
     return mCollectedObjects;
 }
 
 bool
 nsCycleCollector::BeginCollection(nsICycleCollectorListener *aListener)
 {
     // aListener should be Begin()'d before this
+    TimeLog timeLog;
+
     if (mParams.mDoNothing)
         return false;
 
     GCGraphBuilder builder(mGraph, mRuntimes, aListener);
     if (!builder.Initialized())
         return false;
 
-#ifdef COLLECT_TIME_DEBUG
-    PRTime now = PR_Now();
-#endif
     for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
         if (mRuntimes[i])
             mRuntimes[i]->BeginCycleCollection(builder, false);
     }
 
-#ifdef COLLECT_TIME_DEBUG
-    printf("cc: mRuntimes[*]->BeginCycleCollection() took %lldms\n",
-           (PR_Now() - now) / PR_USEC_PER_MSEC);
-
-    now = PR_Now();
-#endif
+    timeLog.Checkpoint("mRuntimes[*]->BeginCycleCollection()");
 
 #ifdef DEBUG_CC
     PRUint32 purpleStart = builder.Count();
 #endif
     mScanInProgress = true;
     SelectPurple(builder);
 #ifdef DEBUG_CC
     PRUint32 purpleEnd = builder.Count();
@@ -2994,45 +3020,26 @@ nsCycleCollector::BeginCollection(nsICyc
         while (i++ < purpleEnd) {
             mStats.mForgetNode++;
             if (mParams.mLogPointers)
                 fprintf(mPtrLog, "F %p\n", queue.GetNext()->mPointer);
         }
     }
 #endif
 
-#ifdef COLLECT_TIME_DEBUG
-    printf("cc: SelectPurple() took %lldms\n",
-           (PR_Now() - now) / PR_USEC_PER_MSEC);
-#endif
+    timeLog.Checkpoint("SelectPurple()");
 
     if (builder.Count() > 0) {
         // The main Bacon & Rajan collection algorithm.
 
-#ifdef COLLECT_TIME_DEBUG
-        now = PR_Now();
-#endif
-
         MarkRoots(builder);
-
-#ifdef COLLECT_TIME_DEBUG
-        {
-            PRTime then = PR_Now();
-            printf("cc: MarkRoots() took %lldms\n",
-                   (then - now) / PR_USEC_PER_MSEC);
-            now = then;
-        }
-#endif
+        timeLog.Checkpoint("MarkRoots()");
 
         ScanRoots();
-
-#ifdef COLLECT_TIME_DEBUG
-        printf("cc: ScanRoots() took %lldms\n",
-               (PR_Now() - now) / PR_USEC_PER_MSEC);
-#endif
+        timeLog.Checkpoint("ScanRoots()");
 
         mScanInProgress = false;
 
         if (aListener) {
             aListener->BeginResults();
 
             NodePool::Enumerator etor(mGraph.mNodes);
             while (!etor.IsDone()) {
@@ -3064,48 +3071,43 @@ nsCycleCollector::BeginCollection(nsICyc
             }
         }
 #endif
 
         for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
             if (mRuntimes[i])
                 mRuntimes[i]->FinishTraverse();
         }
+        timeLog.Checkpoint("mRuntimes[*]->FinishTraverse()");
     }
     else {
         mScanInProgress = false;
     }
 
     return true;
 }
 
 bool
 nsCycleCollector::FinishCollection(nsICycleCollectorListener *aListener)
 {
-#ifdef COLLECT_TIME_DEBUG
-    PRTime now = PR_Now();
-#endif
-
+    TimeLog timeLog;
     bool collected = CollectWhite(aListener);
-
-#ifdef COLLECT_TIME_DEBUG
-    printf("cc: CollectWhite() took %lldms\n",
-           (PR_Now() - now) / PR_USEC_PER_MSEC);
-#endif
+    timeLog.Checkpoint("CollectWhite()");
 
 #ifdef DEBUG_CC
     mStats.mCollection++;
     if (mParams.mReportStats)
         mStats.Dump();
 #endif
 
     for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
         if (mRuntimes[i])
             mRuntimes[i]->FinishCycleCollection();
     }
+    timeLog.Checkpoint("mRuntimes[*]->FinishCycleCollection()");
 
     mFollowupCollection = true;
 
 #ifdef DEBUG_CC
     // We wait until after FinishCollection to check the white nodes because
     // some objects may outlive CollectWhite but then be freed by
     // FinishCycleCollection (like XPConnect's deferred release of native
     // objects).
@@ -3120,16 +3122,17 @@ nsCycleCollector::FinishCollection(nsICy
                    "  means the Unlink implementation was insufficient.\n",
                    pinfo->mName, pinfo->mPointer);
         }
     }
 #endif
 
     mWhiteNodes->Clear();
     ClearGraph();
+    timeLog.Checkpoint("ClearGraph()");
 
     mParams.mDoNothing = false;
 
     return collected;
 }
 
 PRUint32
 nsCycleCollector::SuspectedCount()
@@ -3860,17 +3863,19 @@ nsCycleCollector_setForgetSkippableCallb
     }
 }
 
 void
 nsCycleCollector_forgetSkippable()
 {
     if (sCollector) {
         SAMPLE_LABEL("CC", "nsCycleCollector_forgetSkippable");
+        TimeLog timeLog;
         sCollector->ForgetSkippable();
+        timeLog.Checkpoint("ForgetSkippable()");
     }
 }
 
 PRUint32
 nsCycleCollector_collect(nsICycleCollectorListener *aListener)
 {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     SAMPLE_LABEL("CC", "nsCycleCollector_collect");
--- a/xpcom/base/nsStackWalk.cpp
+++ b/xpcom/base/nsStackWalk.cpp
@@ -36,21 +36,24 @@
  * 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 ***** */
 
 /* API for getting a stack trace of the C/C++ stack on the current thread */
 
 #include "mozilla/Util.h"
+#include "mozilla/StackWalk.h"
 #include "nsDebug.h"
 #include "nsStackWalkPrivate.h"
 
 #include "nsStackWalk.h"
 
+using namespace mozilla;
+
 // The presence of this address is the stack must stop the stack walk. If
 // there is no such address, the structure will be {NULL, true}.
 struct CriticalAddress {
   void* mAddr;
   bool mInit;
 };
 static CriticalAddress gCriticalAddress;
 
@@ -210,18 +213,16 @@ StackWalkInitCriticalAddress()
 // We need a way to know if we are building for WXP (or later), as if we are, we
 // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
 // A value of 9 indicates we want to use the new APIs.
 #if API_VERSION_NUMBER >= 9
 #define USING_WXP_VERSION 1
 #endif
 #endif
 
-using namespace mozilla;
-
 // Define these as static pointers so that we can load the DLL on the
 // fly (and not introduce a link-time dependency on it). Tip o' the
 // hat to Matt Pietrick for this idea. See:
 //
 //   http://msdn.microsoft.com/library/periodic/period97/F1/D3/S245C6.htm
 //
 PR_BEGIN_EXTERN_C
 
@@ -1567,48 +1568,32 @@ NS_FormatCodeAddressDetails(void *aPC, c
              aDetails->library[0] ? aDetails->library : "??",
              aDetails->function[0] ? aDetails->function : "??",
              aDetails->foffset);
     return NS_OK;
 }
 
 #else // not __sun-specific
 
-#define X86_OR_PPC (defined(__i386) || defined(PPC) || defined(__ppc__))
-#if X86_OR_PPC && (NSSTACKWALK_SUPPORTS_MACOSX || NSSTACKWALK_SUPPORTS_LINUX) // i386 or PPC Linux or Mac stackwalking code
-
 #if __GLIBC__ > 2 || __GLIBC_MINOR > 1
 #define HAVE___LIBC_STACK_END 1
 #else
 #define HAVE___LIBC_STACK_END 0
 #endif
 
 #if HAVE___LIBC_STACK_END
 extern void *__libc_stack_end; // from ld-linux.so
 #endif
-
-EXPORT_XPCOM_API(nsresult)
-NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
-             void *aClosure, uintptr_t aThread)
+namespace mozilla {
+nsresult
+FramePointerStackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
+                      void *aClosure, void **bp)
 {
-  MOZ_ASSERT(gCriticalAddress.mInit);
-  MOZ_ASSERT(!aThread);
   // Stack walking code courtesy Kipp's "leaky".
 
-  // Get the frame pointer
-  void **bp;
-#if defined(__i386) 
-  __asm__( "movl %%ebp, %0" : "=g"(bp));
-#else
-  // It would be nice if this worked uniformly, but at least on i386 and
-  // x86_64, it stopped working with gcc 4.1, because it points to the
-  // end of the saved registers instead of the start.
-  bp = (void**) __builtin_frame_address(0);
-#endif
-
   int skip = aSkipFrames;
   while (1) {
     void **next = (void**)*bp;
     // bp may not be a frame pointer on i386 if code was compiled with
     // -fomit-frame-pointer, so do some sanity checks.
     // (bp should be a frame pointer on ppc(64) but checking anyway may help
     // a little if the stack has been corrupted.)
     if (next <= bp ||
@@ -1631,16 +1616,43 @@ NS_StackWalk(NS_WalkStackCallback aCallb
     if (--skip < 0) {
       (*aCallback)(pc, aClosure);
     }
     bp = next;
   }
   return NS_OK;
 }
 
+}
+
+#define X86_OR_PPC (defined(__i386) || defined(PPC) || defined(__ppc__))
+#if X86_OR_PPC && (NSSTACKWALK_SUPPORTS_MACOSX || NSSTACKWALK_SUPPORTS_LINUX) // i386 or PPC Linux or Mac stackwalking code
+
+EXPORT_XPCOM_API(nsresult)
+NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
+             void *aClosure, uintptr_t aThread)
+{
+  MOZ_ASSERT(gCriticalAddress.mInit);
+  MOZ_ASSERT(!aThread);
+
+  // Get the frame pointer
+  void **bp;
+#if defined(__i386) 
+  __asm__( "movl %%ebp, %0" : "=g"(bp));
+#else
+  // It would be nice if this worked uniformly, but at least on i386 and
+  // x86_64, it stopped working with gcc 4.1, because it points to the
+  // end of the saved registers instead of the start.
+  bp = (void**) __builtin_frame_address(0);
+#endif
+  return FramePointerStackWalk(aCallback, aSkipFrames,
+                               aClosure, bp);
+
+}
+
 #elif defined(HAVE__UNWIND_BACKTRACE)
 
 // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
 #include <unwind.h>
 
 struct unwind_info {
     NS_WalkStackCallback callback;
     int skip;