Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Fri, 23 Nov 2012 17:18:45 -0800
changeset 117348 f352f0484a2fb0364f522664ba7a1cc5598798d4
parent 117347 7a3e3dc72d8622557be5b2a2c84d6d8ef6887834 (current diff)
parent 114083 0d373cf880fde56ed9c61a5422b13a09462344df (diff)
child 117349 c097ef134b78750e8ff32ebc911e28c04770dcb6
push id24098
push userrnewman@mozilla.com
push dateThu, 03 Jan 2013 03:39:06 +0000
treeherdermozilla-central@6955309291ee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to s-c.
dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Node-appendChild.html.json
dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Node-constants.html.json
dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Node-contains.xml.json
dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Node-insertBefore.html.json
dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Node-removeChild.html.json
--- a/accessible/src/base/AccEvent.cpp
+++ b/accessible/src/base/AccEvent.cpp
@@ -27,17 +27,20 @@ using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // AccEvent constructors
 
 AccEvent::AccEvent(uint32_t aEventType, Accessible* aAccessible,
                    EIsFromUserInput aIsFromUserInput, EEventRule aEventRule) :
   mEventType(aEventType), mEventRule(aEventRule), mAccessible(aAccessible)
 {
-  CaptureIsFromUserInput(aIsFromUserInput);
+  if (aIsFromUserInput == eAutoDetect)
+    mIsFromUserInput = nsEventStateManager::IsHandlingUserInput();
+  else
+    mIsFromUserInput = aIsFromUserInput == eFromUserInput ? true : false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // AccEvent public methods
 
 already_AddRefed<nsAccEvent>
 AccEvent::CreateXPCOMObject()
 {
@@ -46,52 +49,29 @@ AccEvent::CreateXPCOMObject()
   return event;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // AccEvent cycle collection
 
 NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(AccEvent)
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(AccEvent)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AccEvent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessible)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(AccEvent)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AccEvent)
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAccessible");
   cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mAccessible));
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AccEvent, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AccEvent, Release)
 
 ////////////////////////////////////////////////////////////////////////////////
-// AccEvent protected methods
-
-void
-AccEvent::CaptureIsFromUserInput(EIsFromUserInput aIsFromUserInput)
-{
-  if (aIsFromUserInput != eAutoDetect) {
-    mIsFromUserInput = aIsFromUserInput == eFromUserInput ? true : false;
-    return;
-  }
-
-  DocAccessible* document = mAccessible->Document();
-  if (!document) {
-    NS_ASSERTION(mAccessible == ApplicationAcc(),
-                 "Accessible other than application should always have a doc!");
-    return;
-  }
-
-  mIsFromUserInput =
-    document->PresContext()->EventStateManager()->IsHandlingUserInputExternal();
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
 // AccStateChangeEvent
 ////////////////////////////////////////////////////////////////////////////////
 
 already_AddRefed<nsAccEvent>
 AccStateChangeEvent::CreateXPCOMObject()
 {
   nsAccEvent* event = new nsAccStateChangeEvent(this);
   NS_IF_ADDREF(event);
--- a/accessible/src/base/AccEvent.h
+++ b/accessible/src/base/AccEvent.h
@@ -110,23 +110,16 @@ public:
 
   /**
    * Reference counting and cycle collection.
    */
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AccEvent)
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AccEvent)
 
 protected:
-
-  /**
-   * Determine whether the event is from user input by event state manager if
-   * it's not pointed explicetly.
-   */
-  void CaptureIsFromUserInput(EIsFromUserInput aIsFromUserInput);
-
   bool mIsFromUserInput;
   uint32_t mEventType;
   EEventRule mEventRule;
   nsRefPtr<Accessible> mAccessible;
 
   friend class NotificationController;
   friend class AccReorderEvent;
 };
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -55,22 +55,22 @@ NotificationController::~NotificationCon
 ////////////////////////////////////////////////////////////////////////////////
 // NotificationCollector: AddRef/Release and cycle collection
 
 NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(NotificationController)
 NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(NotificationController)
 
 NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController)
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(NotificationController)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationController)
   if (tmp->mDocument)
     tmp->Shutdown();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(NotificationController)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationController)
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDocument");
   cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mDocument.get()));
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentInsertions)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvents)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController, AddRef)
@@ -861,21 +861,21 @@ NotificationController::ContentInsertion
     node = node->GetNextSibling();
   }
 
   return haveToUpdate;
 }
 
 NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController::ContentInsertion)
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(NotificationController::ContentInsertion)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationController::ContentInsertion)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContainer)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(NotificationController::ContentInsertion)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationController::ContentInsertion)
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContainer");
   cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mContainer.get()));
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController::ContentInsertion,
                                      AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(NotificationController::ContentInsertion,
                                        Release)
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -69,76 +69,76 @@ static const uint32_t kRelationAttrsLen 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Constructor/desctructor
 
 DocAccessible::
   DocAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
                   nsIPresShell* aPresShell) :
   HyperTextAccessibleWrap(aRootContent, this),
-  mDocument(aDocument), mScrollPositionChangedTicks(0),
+  mDocumentNode(aDocument), mScrollPositionChangedTicks(0),
   mLoadState(eTreeConstructionPending), mLoadEventType(0),
   mVirtualCursor(nullptr),
   mPresShell(aPresShell)
 {
   mFlags |= eDocAccessible | eNotNodeMapEntry;
   MOZ_ASSERT(mPresShell, "should have been given a pres shell");
   mPresShell->SetDocAccessible(this);
 
   mDependentIDsHash.Init();
   // XXX aaronl should we use an algorithm for the initial cache size?
   mAccessibleCache.Init(kDefaultCacheSize);
   mNodeToAccessibleMap.Init(kDefaultCacheSize);
 
   // If this is a XUL Document, it should not implement nsHyperText
-  if (mDocument && mDocument->IsXUL())
+  if (mDocumentNode && mDocumentNode->IsXUL())
     mFlags &= ~eHyperTextAccessible;
 
   // For GTK+ native window, we do nothing here.
-  if (!mDocument)
+  if (!mDocumentNode)
     return;
 
   // DocManager creates document accessible when scrollable frame is
   // available already, it should be safe time to add scroll listener.
   AddScrollListener();
 
   // We provide a virtual cursor if this is a root doc or if it's a tab doc.
-  mIsCursorable = (!(mDocument->GetParentDocument()) ||
-                   nsCoreUtils::IsTabDocument(mDocument));
+  mIsCursorable = (!(mDocumentNode->GetParentDocument()) ||
+                   nsCoreUtils::IsTabDocument(mDocumentNode));
 }
 
 DocAccessible::~DocAccessible()
 {
   NS_ASSERTION(!mPresShell, "LastRelease was never called!?!");
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(DocAccessible)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocAccessible, Accessible)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentNode)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationController)
 
   if (tmp->mVirtualCursor) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVirtualCursor)
   }
 
   uint32_t i, length = tmp->mChildDocuments.Length();
   for (i = 0; i < length; ++i) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildDocuments[i])
   }
 
   CycleCollectorTraverseCache(tmp->mAccessibleCache, &cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocAccessible, Accessible)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentNode)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationController)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVirtualCursor)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildDocuments)
   tmp->mDependentIDsHash.Clear();
   tmp->mNodeToAccessibleMap.Clear();
   ClearCache(tmp->mAccessibleCache);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
@@ -201,30 +201,30 @@ DocAccessible::Name(nsString& aName)
   return eNameOK;
 }
 
 // Accessible public method
 role
 DocAccessible::NativeRole()
 {
   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
-    nsCoreUtils::GetDocShellTreeItemFor(mDocument);
+    nsCoreUtils::GetDocShellTreeItemFor(mDocumentNode);
   if (docShellTreeItem) {
     nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
     docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
     int32_t itemType;
     docShellTreeItem->GetItemType(&itemType);
     if (sameTypeRoot == docShellTreeItem) {
       // Root of content or chrome tree
       if (itemType == nsIDocShellTreeItem::typeChrome)
         return roles::CHROME_WINDOW;
 
       if (itemType == nsIDocShellTreeItem::typeContent) {
 #ifdef MOZ_XUL
-        nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
+        nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocumentNode));
         if (xulDoc)
           return roles::APPLICATION;
 #endif
         return roles::DOCUMENT;
       }
     }
     else if (itemType == nsIDocShellTreeItem::typeContent) {
       return roles::DOCUMENT;
@@ -338,88 +338,88 @@ DocAccessible::TakeFocus()
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   // Focus the document.
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
   NS_ENSURE_STATE(fm);
 
   nsCOMPtr<nsIDOMElement> newFocus;
-  return fm->MoveFocus(mDocument->GetWindow(), nullptr,
+  return fm->MoveFocus(mDocumentNode->GetWindow(), nullptr,
                        nsIFocusManager::MOVEFOCUS_ROOT, 0,
                        getter_AddRefs(newFocus));
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessibleDocument
 
 NS_IMETHODIMP
 DocAccessible::GetURL(nsAString& aURL)
 {
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsISupports> container = mDocument->GetContainer();
+  nsCOMPtr<nsISupports> container = mDocumentNode->GetContainer();
   nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(container));
   nsAutoCString theURL;
   if (webNav) {
     nsCOMPtr<nsIURI> pURI;
     webNav->GetCurrentURI(getter_AddRefs(pURI));
     if (pURI)
       pURI->GetSpec(theURL);
   }
   CopyUTF8toUTF16(theURL, aURL);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocAccessible::GetTitle(nsAString& aTitle)
 {
-  nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(mDocument);
+  nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(mDocumentNode);
   if (!domDocument) {
     return NS_ERROR_FAILURE;
   }
   return domDocument->GetTitle(aTitle);
 }
 
 NS_IMETHODIMP
 DocAccessible::GetMimeType(nsAString& aMimeType)
 {
-  nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(mDocument);
+  nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(mDocumentNode);
   if (!domDocument) {
     return NS_ERROR_FAILURE;
   }
   return domDocument->GetContentType(aMimeType);
 }
 
 NS_IMETHODIMP
 DocAccessible::GetDocType(nsAString& aDocType)
 {
-  nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mDocument));
+  nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mDocumentNode));
   nsCOMPtr<nsIDOMDocumentType> docType;
 
 #ifdef MOZ_XUL
-  nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
+  nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocumentNode));
   if (xulDoc) {
     aDocType.AssignLiteral("window"); // doctype not implemented for XUL at time of writing - causes assertion
     return NS_OK;
   } else
 #endif
   if (domDoc && NS_SUCCEEDED(domDoc->GetDoctype(getter_AddRefs(docType))) && docType) {
     return docType->GetPublicId(aDocType);
   }
 
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 DocAccessible::GetNameSpaceURIForID(int16_t aNameSpaceID, nsAString& aNameSpaceURI)
 {
-  if (mDocument) {
+  if (mDocumentNode) {
     nsCOMPtr<nsINameSpaceManager> nameSpaceManager =
         do_GetService(NS_NAMESPACEMANAGER_CONTRACTID);
     if (nameSpaceManager)
       return nameSpaceManager->GetNameSpaceURI(aNameSpaceID, aNameSpaceURI);
   }
   return NS_ERROR_FAILURE;
 }
 
@@ -430,37 +430,37 @@ DocAccessible::GetWindowHandle(void** aW
   *aWindow = GetNativeWindow();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocAccessible::GetWindow(nsIDOMWindow** aDOMWin)
 {
   *aDOMWin = nullptr;
-  if (!mDocument) {
+  if (!mDocumentNode) {
     return NS_ERROR_FAILURE;  // Accessible is Shutdown()
   }
-  *aDOMWin = mDocument->GetWindow();
+  *aDOMWin = mDocumentNode->GetWindow();
 
   if (!*aDOMWin)
     return NS_ERROR_FAILURE;  // No DOM Window
 
   NS_ADDREF(*aDOMWin);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocAccessible::GetDOMDocument(nsIDOMDocument** aDOMDocument)
 {
   NS_ENSURE_ARG_POINTER(aDOMDocument);
   *aDOMDocument = nullptr;
 
-  if (mDocument)
-    CallQueryInterface(mDocument, aDOMDocument);
+  if (mDocumentNode)
+    CallQueryInterface(mDocumentNode, aDOMDocument);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocAccessible::GetParentDocument(nsIAccessibleDocument** aDocument)
 {
   NS_ENSURE_ARG_POINTER(aDocument);
@@ -520,27 +520,27 @@ DocAccessible::GetVirtualCursor(nsIAcces
 }
 
 // HyperTextAccessible method
 already_AddRefed<nsIEditor>
 DocAccessible::GetEditor() const
 {
   // Check if document is editable (designMode="on" case). Otherwise check if
   // the html:body (for HTML document case) or document element is editable.
-  if (!mDocument->HasFlag(NODE_IS_EDITABLE) &&
+  if (!mDocumentNode->HasFlag(NODE_IS_EDITABLE) &&
       (!mContent || !mContent->HasFlag(NODE_IS_EDITABLE)))
     return nullptr;
 
-  nsCOMPtr<nsISupports> container = mDocument->GetContainer();
+  nsCOMPtr<nsISupports> container = mDocumentNode->GetContainer();
   nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(container));
   if (!editingSession)
     return nullptr; // No editing session interface
 
   nsCOMPtr<nsIEditor> editor;
-  editingSession->GetEditorForWindow(mDocument->GetWindow(), getter_AddRefs(editor));
+  editingSession->GetEditorForWindow(mDocumentNode->GetWindow(), getter_AddRefs(editor));
   if (!editor)
     return nullptr;
 
   bool isEditable = false;
   editor->GetIsDocumentEditable(&isEditable);
   if (isEditable)
     return editor.forget();
 
@@ -578,57 +578,57 @@ DocAccessible::GetAccessible(nsINode* aN
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessNode
 
 void
 DocAccessible::Init()
 {
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eDocCreate))
-    logging::DocCreate("document initialize", mDocument, this);
+    logging::DocCreate("document initialize", mDocumentNode, this);
 #endif
 
   // Initialize notification controller.
   mNotificationController = new NotificationController(this, mPresShell);
 
   // Mark the document accessible as loaded if its DOM document was loaded at
   // this point (this can happen because a11y is started late or DOM document
   // having no container was loaded.
-  if (mDocument->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE)
+  if (mDocumentNode->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE)
     mLoadState |= eDOMLoaded;
 
   AddEventListeners();
 }
 
 void
 DocAccessible::Shutdown()
 {
   if (!mPresShell) // already shutdown
     return;
 
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eDocDestroy))
-    logging::DocDestroy("document shutdown", mDocument, this);
+    logging::DocDestroy("document shutdown", mDocumentNode, this);
 #endif
 
   mPresShell->SetDocAccessible(nullptr);
 
   if (mNotificationController) {
     mNotificationController->Shutdown();
     mNotificationController = nullptr;
   }
 
   RemoveEventListeners();
 
   // Mark the document as shutdown before AT is notified about the document
   // removal from its container (valid for root documents on ATK and due to
   // some reason for MSAA, refer to bug 757392 for details).
   mFlags |= eIsDefunct;
-  nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocument;
-  mDocument = nullptr;
+  nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocumentNode;
+  mDocumentNode = nullptr;
 
   if (mParent) {
     DocAccessible* parentDocument = mParent->Document();
     if (parentDocument)
       parentDocument->RemoveChildDocument(this);
 
     mParent->RemoveChild(this);
   }
@@ -668,17 +668,17 @@ DocAccessible::GetFrame() const
 }
 
 // DocAccessible protected member
 void
 DocAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aRelativeFrame)
 {
   *aRelativeFrame = GetFrame();
 
-  nsIDocument *document = mDocument;
+  nsIDocument *document = mDocumentNode;
   nsIDocument *parentDoc = nullptr;
 
   while (document) {
     nsIPresShell *presShell = document->GetShell();
     if (!presShell) {
       return;
     }
 
@@ -713,17 +713,17 @@ DocAccessible::GetBoundsRect(nsRect& aBo
 nsresult
 DocAccessible::AddEventListeners()
 {
   // 1) Set up scroll position listener
   // 2) Check for editor and listen for changes to editor
 
   NS_ENSURE_TRUE(mPresShell, NS_ERROR_FAILURE);
 
-  nsCOMPtr<nsISupports> container = mDocument->GetContainer();
+  nsCOMPtr<nsISupports> container = mDocumentNode->GetContainer();
   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(container));
   NS_ENSURE_TRUE(docShellTreeItem, NS_ERROR_FAILURE);
 
   // Make sure we're a content docshell
   // We don't want to listen to chrome progress
   int32_t itemType;
   docShellTreeItem->GetItemType(&itemType);
 
@@ -744,34 +744,34 @@ DocAccessible::AddEventListeners()
     NS_ENSURE_TRUE(rootAccessible, NS_ERROR_FAILURE);
     nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
     if (caretAccessible) {
       caretAccessible->AddDocSelectionListener(mPresShell);
     }
   }
 
   // add document observer
-  mDocument->AddObserver(this);
+  mDocumentNode->AddObserver(this);
   return NS_OK;
 }
 
 // DocAccessible protected member
 nsresult
 DocAccessible::RemoveEventListeners()
 {
   // Remove listeners associated with content documents
   // Remove scroll position listener
   RemoveScrollListener();
 
-  NS_ASSERTION(mDocument, "No document during removal of listeners.");
+  NS_ASSERTION(mDocumentNode, "No document during removal of listeners.");
 
-  if (mDocument) {
-    mDocument->RemoveObserver(this);
+  if (mDocumentNode) {
+    mDocumentNode->RemoveObserver(this);
 
-    nsCOMPtr<nsISupports> container = mDocument->GetContainer();
+    nsCOMPtr<nsISupports> container = mDocumentNode->GetContainer();
     nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(container));
     NS_ASSERTION(docShellTreeItem, "doc should support nsIDocShellTreeItem.");
 
     if (docShellTreeItem) {
       int32_t itemType;
       docShellTreeItem->GetItemType(&itemType);
       if (itemType == nsIDocShellTreeItem::typeContent) {
         nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
@@ -1493,17 +1493,17 @@ DocAccessible::ProcessInvalidationList()
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible protected
 
 void
 DocAccessible::CacheChildren()
 {
   // Search for accessible children starting from the document element since
   // some web pages tend to insert elements under it rather than document body.
-  TreeWalker walker(this, mDocument->GetRootElement());
+  TreeWalker walker(this, mDocumentNode->GetRootElement());
 
   Accessible* child = nullptr;
   while ((child = walker.NextChild()) && AppendChild(child));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Protected members
 
@@ -1536,17 +1536,17 @@ DocAccessible::NotifyOfLoading(bool aIsR
 void
 DocAccessible::DoInitialUpdate()
 {
   mLoadState |= eTreeConstructed;
 
   // The content element may be changed before the initial update and then we
   // miss the notification (since content tree change notifications are ignored
   // prior to initial update). Make sure the content element is valid.
-  nsIContent* contentElm = nsCoreUtils::GetRoleContent(mDocument);
+  nsIContent* contentElm = nsCoreUtils::GetRoleContent(mDocumentNode);
   if (mContent != contentElm)
     mContent = contentElm;
 
   // Build initial tree.
   CacheChildrenInSubtree(this);
 
   // Fire reorder event after the document tree is constructed. Note, since
   // this reorder event is processed by parent document then events targeted to
@@ -1758,17 +1758,17 @@ DocAccessible::ProcessContentInserted(Ac
     if (presentContainer != aContainer)
       continue;
 
     if (containerNotUpdated) {
       containerNotUpdated = false;
 
       if (aContainer == this) {
         // If new root content has been inserted then update it.
-        nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocument);
+        nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocumentNode);
         if (rootContent != mContent)
           mContent = rootContent;
 
         // Continue to update the tree even if we don't have root content.
         // For example, elements may be inserted under the document element while
         // there is no HTML body element.
       }
 
@@ -1807,16 +1807,33 @@ DocAccessible::UpdateTree(Accessible* aC
   }
 #endif
 
   nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
 
   if (child) {
     updateFlags |= UpdateTreeInternal(child, aIsInsert, reorderEvent);
 
+    // XXX: since select change insertion point of option contained by optgroup
+    // then we need to have special processing for them (bug 690417).
+    if (!aIsInsert && aChildNode->IsHTML(nsGkAtoms::optgroup) &&
+        aContainer->GetContent()->IsHTML(nsGkAtoms::select)) {
+      for (nsIContent* optContent = aChildNode->GetFirstChild(); optContent;
+           optContent = optContent->GetNextSibling()) {
+        if (optContent->IsHTML(nsGkAtoms::option)) {
+          Accessible* option = GetAccessible(optContent);
+          if (option) {
+            NS_ASSERTION(option->Parent() == aContainer,
+                         "Not expected hierarchy on HTML select!");
+            if (option->Parent() == aContainer)
+              updateFlags |= UpdateTreeInternal(option, aIsInsert, reorderEvent);
+          }
+        }
+      }
+    }
   } else {
     TreeWalker walker(aContainer, aChildNode, true);
 
     while ((child = walker.NextChild()))
       updateFlags |= UpdateTreeInternal(child, aIsInsert, reorderEvent);
   }
 
   // Content insertion/removal is not cause of accessible tree change.
@@ -1984,17 +2001,17 @@ DocAccessible::ShutdownChildrenInSubtree
   }
 
   UnbindFromDocument(aAccessible);
 }
 
 bool
 DocAccessible::IsLoadEventTarget() const
 {
-  nsCOMPtr<nsISupports> container = mDocument->GetContainer();
+  nsCOMPtr<nsISupports> container = mDocumentNode->GetContainer();
   nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(container);
   NS_ASSERTION(treeItem, "No document shell for document!");
 
   nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
   treeItem->GetParent(getter_AddRefs(parentTreeItem));
 
   // Not a root document.
   if (parentTreeItem) {
--- a/accessible/src/generic/DocAccessible.h
+++ b/accessible/src/generic/DocAccessible.h
@@ -74,18 +74,18 @@ public:
 
   // nsIDocumentObserver
   NS_DECL_NSIDOCUMENTOBSERVER
 
   // nsAccessNode
   virtual void Init();
   virtual void Shutdown();
   virtual nsIFrame* GetFrame() const;
-  virtual nsINode* GetNode() const { return mDocument; }
-  nsIDocument* DocumentNode() const { return mDocument; }
+  virtual nsINode* GetNode() const { return mDocumentNode; }
+  nsIDocument* DocumentNode() const { return mDocumentNode; }
 
   // Accessible
   virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
   virtual void Description(nsString& aDescription);
   virtual Accessible* FocusedChild();
   virtual mozilla::a11y::role NativeRole();
   virtual uint64_t NativeState();
   virtual uint64_t NativeInteractiveState() const;
@@ -117,18 +117,18 @@ public:
   /**
    * Return true if associated DOM document was loaded and isn't unloading.
    */
   bool IsContentLoaded() const
   {
     // eDOMLoaded flag check is used for error pages as workaround to make this
     // method return correct result since error pages do not receive 'pageshow'
     // event and as consequence nsIDocument::IsShowing() returns false.
-    return mDocument && mDocument->IsVisible() &&
-      (mDocument->IsShowing() || HasLoadState(eDOMLoaded));
+    return mDocumentNode && mDocumentNode->IsVisible() &&
+      (mDocumentNode->IsShowing() || HasLoadState(eDOMLoaded));
   }
 
   /**
    * Document load states.
    */
   enum LoadState {
     // initial tree construction is pending
     eTreeConstructionPending = 0,
@@ -484,17 +484,17 @@ protected:
 
   /**
    * Cache of accessibles within this document accessible.
    */
   AccessibleHashtable mAccessibleCache;
   nsDataHashtable<nsPtrHashKey<const nsINode>, Accessible*>
     mNodeToAccessibleMap;
 
-    nsCOMPtr<nsIDocument> mDocument;
+    nsCOMPtr<nsIDocument> mDocumentNode;
     nsCOMPtr<nsITimer> mScrollWatchTimer;
     uint16_t mScrollPositionChangedTicks; // Used for tracking scroll events
 
   /**
    * Bit mask of document load states (@see LoadState).
    */
   uint32_t mLoadState;
 
--- a/accessible/src/generic/RootAccessible.cpp
+++ b/accessible/src/generic/RootAccessible.cpp
@@ -77,44 +77,44 @@ RootAccessible::Name(nsString& aName)
   aName.Truncate();
 
   if (mRoleMapEntry) {
     Accessible::Name(aName);
     if (!aName.IsEmpty())
       return eNameOK;
   }
 
-  nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(mDocument);
+  nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(mDocumentNode);
   NS_ENSURE_TRUE(document, eNameOK);
   document->GetTitle(aName);
   return eNameOK;
 }
 
 role
 RootAccessible::NativeRole()
 {
   // If it's a <dialog> or <wizard>, use roles::DIALOG instead
-  dom::Element* rootElm = mDocument->GetRootElement();
+  dom::Element* rootElm = mDocumentNode->GetRootElement();
   if (rootElm && (rootElm->Tag() == nsGkAtoms::dialog ||
                   rootElm->Tag() == nsGkAtoms::wizard))
     return roles::DIALOG;
 
   return DocAccessibleWrap::NativeRole();
 }
 
 // RootAccessible protected member
 #ifdef MOZ_XUL
 uint32_t
 RootAccessible::GetChromeFlags()
 {
   // Return the flag set for the top level window as defined 
   // by nsIWebBrowserChrome::CHROME_WINDOW_[FLAGNAME]
   // Not simple: nsIXULWindow is not just a QI from nsIDOMWindow
   nsCOMPtr<nsIDocShellTreeItem> treeItem =
-    nsCoreUtils::GetDocShellTreeItemFor(mDocument);
+    nsCoreUtils::GetDocShellTreeItemFor(mDocumentNode);
   NS_ENSURE_TRUE(treeItem, 0);
   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   treeItem->GetTreeOwner(getter_AddRefs(treeOwner));
   NS_ENSURE_TRUE(treeOwner, 0);
   nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwner));
   if (!xulWin) {
     return 0;
   }
@@ -140,17 +140,17 @@ RootAccessible::NativeState()
     //     how to detect that
   if (chromeFlags & nsIWebBrowserChrome::CHROME_TITLEBAR)
     state |= states::MOVEABLE;
   if (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)
     state |= states::MODAL;
 #endif
 
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
-  if (fm && fm->GetActiveWindow() == mDocument->GetWindow())
+  if (fm && fm->GetActiveWindow() == mDocumentNode->GetWindow())
     state |= states::ACTIVE;
 
   return state;
 }
 
 const char* const docEvents[] = {
 #ifdef DEBUG_DRAGDROPSTART
   // Capture mouse over events and fire fake DRAGDROPSTART event to simplify
@@ -182,17 +182,17 @@ const char* const docEvents[] = {
 
 nsresult
 RootAccessible::AddEventListeners()
 {
   // nsIDOMEventTarget interface allows to register event listeners to
   // receive untrusted events (synthetic events generated by untrusted code).
   // For example, XBL bindings implementations for elements that are hosted in
   // non chrome document fire untrusted events.
-  nsCOMPtr<nsIDOMEventTarget> nstarget(do_QueryInterface(mDocument));
+  nsCOMPtr<nsIDOMEventTarget> nstarget(do_QueryInterface(mDocumentNode));
 
   if (nstarget) {
     for (const char* const* e = docEvents,
                    * const* e_end = ArrayEnd(docEvents);
          e < e_end; ++e) {
       nsresult rv = nstarget->AddEventListener(NS_ConvertASCIItoUTF16(*e),
                                                this, true, true, 2);
       NS_ENSURE_SUCCESS(rv, rv);
@@ -204,17 +204,17 @@ RootAccessible::AddEventListeners()
   }
 
   return DocAccessible::AddEventListeners();
 }
 
 nsresult
 RootAccessible::RemoveEventListeners()
 {
-  nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(mDocument));
+  nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(mDocumentNode));
   if (target) { 
     for (const char* const* e = docEvents,
                    * const* e_end = ArrayEnd(docEvents);
          e < e_end; ++e) {
       nsresult rv = target->RemoveEventListener(NS_ConvertASCIItoUTF16(*e), this, true);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
@@ -503,20 +503,20 @@ RootAccessible::Shutdown()
 
   DocAccessibleWrap::Shutdown();
 }
 
 // nsIAccessible method
 Relation
 RootAccessible::RelationByType(uint32_t aType)
 {
-  if (!mDocument || aType != nsIAccessibleRelation::RELATION_EMBEDS)
+  if (!mDocumentNode || aType != nsIAccessibleRelation::RELATION_EMBEDS)
     return DocAccessibleWrap::RelationByType(aType);
 
-  nsIDOMWindow* rootWindow = mDocument->GetWindow();
+  nsIDOMWindow* rootWindow = mDocumentNode->GetWindow();
   if (rootWindow) {
     nsCOMPtr<nsIDOMWindow> contentWindow;
     rootWindow->GetContent(getter_AddRefs(contentWindow));
     if (contentWindow) {
       nsCOMPtr<nsIDOMDocument> contentDOMDocument;
       contentWindow->GetDocument(getter_AddRefs(contentDOMDocument));
       nsCOMPtr<nsIDocument> contentDocumentNode =
         do_QueryInterface(contentDOMDocument);
--- a/accessible/src/msaa/DocAccessibleWrap.cpp
+++ b/accessible/src/msaa/DocAccessibleWrap.cpp
@@ -217,17 +217,17 @@ DocAccessibleWrap::get_accValue(
 // nsAccessNode
 
 void
 DocAccessibleWrap::Shutdown()
 {
   // Do window emulation specific shutdown if emulation was started.
   if (nsWinUtils::IsWindowEmulationStarted()) {
     // Destroy window created for root document.
-    if (nsCoreUtils::IsTabDocument(mDocument)) {
+    if (nsCoreUtils::IsTabDocument(mDocumentNode)) {
       sHWNDCache.Remove(mHWND);
       ::DestroyWindow(static_cast<HWND>(mHWND));
     }
 
     mHWND = nullptr;
   }
 
   DocAccessible::Shutdown();
@@ -247,19 +247,19 @@ DocAccessibleWrap::GetNativeWindow() con
 
 void
 DocAccessibleWrap::DoInitialUpdate()
 {
   DocAccessible::DoInitialUpdate();
 
   if (nsWinUtils::IsWindowEmulationStarted()) {
     // Create window for tab document.
-    if (nsCoreUtils::IsTabDocument(mDocument)) {
+    if (nsCoreUtils::IsTabDocument(mDocumentNode)) {
       mozilla::dom::TabChild* tabChild =
-        mozilla::dom::GetTabChildFrom(mDocument->GetShell());
+        mozilla::dom::GetTabChildFrom(mDocumentNode->GetShell());
 
       a11y::RootAccessible* rootDocument = RootAccessible();
 
       mozilla::WindowsHandle nativeData = NULL;
       if (tabChild)
         tabChild->SendGetWidgetNativeData(&nativeData);
       else
         nativeData = reinterpret_cast<mozilla::WindowsHandle>(
@@ -269,17 +269,17 @@ DocAccessibleWrap::DoInitialUpdate()
       int32_t x = CW_USEDEFAULT, y = CW_USEDEFAULT, width = 0, height = 0;
       if (Compatibility::IsDolphin()) {
         GetBounds(&x, &y, &width, &height);
         int32_t rootX = 0, rootY = 0, rootWidth = 0, rootHeight = 0;
         rootDocument->GetBounds(&rootX, &rootY, &rootWidth, &rootHeight);
         x = rootX - x;
         y -= rootY;
 
-        nsCOMPtr<nsISupports> container = mDocument->GetContainer();
+        nsCOMPtr<nsISupports> container = mDocumentNode->GetContainer();
         nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
         docShell->GetIsActive(&isActive);
       }
 
       HWND parentWnd = reinterpret_cast<HWND>(nativeData);
       mHWND = nsWinUtils::CreateNativeWindow(kClassNameTabContent, parentWnd,
                                              x, y, width, height, isActive);
 
--- a/b2g/config/panda/releng-pandaboard.tt
+++ b/b2g/config/panda/releng-pandaboard.tt
@@ -1,8 +1,8 @@
 [
 {
-"size": 648867404,
-"digest": "4d11978695ca3e3086a872b48d5bb0cff8cc01a8bfa4fcf4a341f84336bdfe272c1665ea79967e4466bcdc247ce411c1d67aef3c3fe8ddf4ab181377aa942824",
+"size": 649879548,
+"digest": "fc46852ab103659df39d3675366068bf12cacf36f0b01be3f919f1a59c35f40df9ff7d5c5d70af41a43bd2312ed105c254615b8f7b1f1b8f291333739843da1e",
 "algorithm": "sha512",
 "filename": "gonk.tar.xz"
 }
 ]
--- a/b2g/config/panda/sources.xml
+++ b/b2g/config/panda/sources.xml
@@ -8,17 +8,17 @@
   <remote fetch="git://github.com/mozilla/" name="mozilla"/>
   <default remote="linaro" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
 
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="273ba23d5c6c9f6a34995a3cc429804d1449ca9f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7c0a6ad7303d3ebc270e29a8b410554d6c632dfb"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="aafa9ca4d2e9e755fe9964018a9797eac4ecc7de"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="32106d4ea635ebe17a1610b643b398db639b8b97"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="6ee1f8987ef36d688f97064c003ad57849dfadf2"/>
 
   <!-- Stock Android things -->
   <!-- Information: platform/abi/cpp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <!-- Information: platform/bionic is tagged with android-4.0.4_r2.1 --><project name="platform/bionic" path="bionic" revision="3d11bf0f3f3cf848f6f1e8449bf8736d8d1c78a3"/>
   <!-- Information: platform/bootable/recovery is tagged with android-4.0.4_r2.1 --><project name="platform/bootable/recovery" path="bootable/recovery" revision="fadc5ac81d6400ebdd041f7d4ea64021596d6b7d"/>
   <!-- Information: device/common is tagged with android-sdk-adt_r20 --><project name="device/common" path="device/common" revision="7d4526582f88808a3194e1a3b304abb369d2745c"/>
@@ -33,42 +33,42 @@
   <!-- Information: platform/external/dbus is tagged with android-4.1.1_r6.1 --><project name="platform/external/dbus" path="external/dbus" revision="537eaff5de9aace3348436166d4cde7adc1e488e"/>
   <!-- Information: platform/external/dhcpcd is tagged with android-sdk-adt_r20 --><project name="platform/external/dhcpcd" path="external/dhcpcd" revision="ddaa48f57b54b2862b3e6dcf18a44c9647f3baaa"/>
   <!-- Information: platform/external/dnsmasq is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/dnsmasq" path="external/dnsmasq" revision="f621afad94df46204c25fc2593a19d704d2637f5"/>
   <!-- Information: platform/external/expat is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/expat" path="external/expat" revision="6df134250feab71edb5915ecaa6268210bca76c5"/>
   <!-- Information: platform/external/fdlibm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/fdlibm" path="external/fdlibm" revision="988ffeb12a6e044ae3504838ef1fee3fe0716934"/>
   <!-- Information: platform/external/flac is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/flac" path="external/flac" revision="5893fbe890f5dab8e4146d2baa4bd2691c0739e0"/>
   <!-- Information: platform/external/freetype is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/freetype" path="external/freetype" revision="aeb407daf3711a10a27f3bc2223c5eb05158076e"/>
   <!-- Information: platform/external/giflib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/giflib" path="external/giflib" revision="b2597268aef084202a8c349d1cc072c03c6e22eb"/>
-  <project name="platform/external/gtest" path="external/gtest" remote="linaro" revision="85030ffa1dd68c0cc05e9115430f28627996e019"/>
+  <!-- Information: platform/external/gtest is tagged with android-4.2_r1 --><project name="platform/external/gtest" path="external/gtest" remote="linaro" revision="344e5f3db17615cc853073a02968a603efd39109"/>
   <!-- Information: platform/external/harfbuzz is tagged with android-sdk-adt_r20 --><project name="platform/external/harfbuzz" path="external/harfbuzz" revision="bae491c03a00757d83ede8d855b7d85d246bde3d"/>
   <!-- Information: platform/external/icu4c is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/icu4c" path="external/icu4c" revision="0fa67b93b831c6636ca18b152a1b1b14cc99b034"/>
   <!-- Information: platform/external/iptables is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/iptables" path="external/iptables" revision="3b2deb17f065c5664bb25e1a28489e5792eb63ff"/>
   <!-- Information: platform/external/jhead is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/jhead" path="external/jhead" revision="754078052c687f6721536009c816644c73e4f145"/>
   <!-- Information: platform/external/jpeg is tagged with android-4.1.1_r6.1 --><project name="platform/external/jpeg" path="external/jpeg" revision="d4fad7f50f79626455d88523207e05b868819cd8"/>
   <!-- Information: platform/external/libgsm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/libgsm" path="external/libgsm" revision="5e4516958690b9a1b2c98f88eeecba3edd2dbda4"/>
-  <!-- Information: platform/external/liblzf is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/liblzf" path="external/liblzf" revision="6946aa575b0949d045722794850896099d937cbb"/>
+  <!-- Information: platform/external/liblzf is tagged with android-4.2_r1 --><project name="platform/external/liblzf" path="external/liblzf" revision="6946aa575b0949d045722794850896099d937cbb"/>
   <!-- Information: platform/external/libnfc-nxp is tagged with android-4.0.4_r2.1 --><project name="platform/external/libnfc-nxp" path="external/libnfc-nxp" revision="533c14450e6239cce8acb74f4e4dea2c89f8f219"/>
-  <!-- Information: platform/external/libnl-headers is tagged with android-4.2_pre3 --><project name="platform/external/libnl-headers" path="external/libnl-headers" revision="6ccf7349d61f73ac26a0675d735d903ab919c658"/>
+  <!-- Information: platform/external/libnl-headers is tagged with android-4.2_r1 --><project name="platform/external/libnl-headers" path="external/libnl-headers" revision="6ccf7349d61f73ac26a0675d735d903ab919c658"/>
   <!-- Information: platform/external/libphonenumber is tagged with android-4.0.4_r2.1 --><project name="platform/external/libphonenumber" path="external/libphonenumber" revision="d470984844c388d6766c3de6ac64e93e00480fc9"/>
   <!-- Information: platform/external/libpng is tagged with android-4.0.4_r2.1 --><project name="platform/external/libpng" path="external/libpng" revision="84d92c718ab9f48faec0f640747c4b6f7a995607"/>
   <!-- Information: platform/external/libvpx is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/libvpx" path="external/libvpx" revision="3a40da0d96da5c520e7707aa14f48a80956e20d7"/>
   <!-- Information: platform/external/mksh is tagged with M8960AAAAANLYA1099D --><project name="platform/external/mksh" path="external/mksh" revision="5155f1c7438ef540d7b25eb70aa1639579795b07"/>
   <project name="platform_external_opensans" path="external/opensans" remote="b2g" revision="b5b4c226ca1d71e936153cf679dda6d3d60e2354"/>
   <!-- Information: platform/external/openssl is tagged with android-4.0.4_r2.1 --><project name="platform/external/openssl" path="external/openssl" revision="ce96fb211b9a44bbd7fb5ef7ed0e6c1244045c2e"/>
   <!-- Information: platform/external/protobuf is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/protobuf" path="external/protobuf" revision="e217977611c52bccde7f7c78e1d3c790c6357431"/>
   <!-- Information: platform/external/safe-iop is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/safe-iop" path="external/safe-iop" revision="07073634e2e3aa4f518e36ed5dec3aabc549d5fb"/>
   <project name="screencap-gonk" path="external/screencap-gonk" remote="b2g" revision="e6403c71e9eca8cb943739d5a0a192deac60fc51"/>
   <!-- Information: platform/external/skia is tagged with android-4.0.4_r2.1 --><project name="platform/external/skia" path="external/skia" revision="5c67a309e16bffe7013defda8f1217b3ce2420b4"/>
   <!-- Information: platform/external/sonivox is tagged with android-sdk-adt_r20 --><project name="platform/external/sonivox" path="external/sonivox" revision="5f9600971859fe072f31b38a51c38157f5f9b381"/>
   <!-- Information: platform/external/speex is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/speex" path="external/speex" revision="ebe6230a7f7c69f5a4389f2b09b7b19ef9e94f32"/>
   <!-- Information: platform/external/sqlite is tagged with android-4.0.4_r2.1 --><project name="platform/external/sqlite" path="external/sqlite" revision="c999ff8c12a4cf81cb9ad628f47b2720effba5e5"/>
   <!-- Information: platform/external/stlport is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/stlport" path="external/stlport" revision="a6734e0645fce81c9610de0488b729207bfa576e"/>
   <!-- Information: platform/external/strace is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/strace" path="external/strace" revision="c9fd2e5ef7d002e12e7cf2512506c84a9414b0fd"/>
-  <!-- Information: platform/external/tagsoup is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/tagsoup" path="external/tagsoup" revision="68c2ec9e0acdb3214b7fb91dbab8c9fab8736817"/>
+  <!-- Information: platform/external/tagsoup is tagged with android-4.2_r1 --><project name="platform/external/tagsoup" path="external/tagsoup" revision="68c2ec9e0acdb3214b7fb91dbab8c9fab8736817"/>
   <!-- Information: platform/external/tinyalsa is tagged with android-4.0.4_r2.1 --><project name="platform/external/tinyalsa" path="external/tinyalsa" revision="495239e683a728957c890c124b239f9b7b8ef5a8"/>
   <!-- Information: platform/external/tremolo is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/tremolo" path="external/tremolo" revision="25bd78d2392dbdc879ae53382cde9d019f79cf6f"/>
   <!-- Information: platform/external/webp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/webp" path="external/webp" revision="88fe2b83c4b9232cd08729556fd0485d6a6a92cd"/>
   <!-- Information: platform/external/webrtc is tagged with android-sdk-adt_r20 --><project name="platform/external/webrtc" path="external/webrtc" revision="4b6dc1ec58105d17dc8c2f550124cc0621dc93b7"/>
   <!-- Information: platform/external/wpa_supplicant is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/wpa_supplicant" path="external/wpa_supplicant" revision="a01d37870bbf9892d43e792e5de0683ca41c5497"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="6dd24fc3792d71edccef9b09140f9a44b063a553"/>
   <!-- Information: platform/external/zlib is tagged with android-4.0.4_r2.1 --><project name="platform/external/zlib" path="external/zlib" revision="69e5801bd16a495e1c1666669fe827b1ddb8d56b"/>
   <!-- Information: platform/external/yaffs2 is tagged with android-4.0.4-aah_r1 --><project name="platform/external/yaffs2" path="external/yaffs2" revision="6232e2d5ab34a40d710e4b05ab0ec6e3727804e7"/>
@@ -84,15 +84,16 @@
   <!-- Information: platform/system/bluetooth is tagged with android-4.0.4_r2.1 --><project name="platform/system/bluetooth" path="system/bluetooth" revision="2588cd802f322650ed737dfb7a10e9ad94064e99"/>
   <!-- Information: platform/system/core is tagged with android-4.0.4_r2.1 --><project name="platform/system/core" path="system/core" revision="c2db4ffb874783220abf967ca4ccd0e6cf1ba57f"/>
   <!-- Information: platform/system/extras is tagged with android-4.0.4_r2.1 --><project name="platform/system/extras" path="system/extras" revision="fa351ab265957fa8815df3c4ca1f3c105f253e8b"/>
   <!-- Information: platform/system/media is tagged with android-4.0.4_r2.1 --><project name="platform/system/media" path="system/media" revision="a8eea50f80327f15cb04bbdfee2d1cfcc4c3ce4a"/>
   <!-- Information: platform/system/netd is tagged with android-4.0.4_r2.1 --><project name="platform/system/netd" path="system/netd" revision="3c903b555975fa59d6688a0a6417ac7512c202e7"/>
   <!-- Information: platform/system/vold is tagged with android-4.0.4_r2.1 --><project name="platform/system/vold" path="system/vold" revision="3ad9072a5d6f6bda32123b367545649364e3c11d"/>
 
   <!-- Pandaboard specific things -->
-  <project name="android-device-panda" path="device/ti/panda" remote="b2g" revision="122c4a65e7d0328a90e1a0596c914377d66a4d6f"/>
+  <project name="android-device-panda" path="device/ti/panda" remote="b2g" revision="599e736cb24dff10b949edcac3a6e5a76bce2144"/>
   <!-- Information: platform/hardware/ti/omap4xxx is tagged with android-4.0.4_r2.1 --><project name="platform/hardware/ti/omap4xxx" path="hardware/ti/omap4xxx" revision="8be8e9a68c96b6cf43c08a58e7ecd7708737c599"/>
   <project name="platform/hardware/ti/wlan" path="hardware/ti/wlan" revision="60dfeb6e4448bfed707946ebca6612980f525e69"/>
   <project name="platform/hardware/ti/wpan" path="hardware/ti/wpan" revision="3ece7d9e08052989401e008bc397dbcd2557cfd0"/>
   <project name="Negatus" path="external/negatus" remote="mozilla" revision="5d5288e7bec67d0a6a29320308cccb1321062b58"/>
+  <project name="orangutan" path="external/orangutan" remote="b2g" revision="e1f222b66f81ed1bc0109d0d1536a337454c8a7b"/>
 
 </manifest>
--- a/b2g/config/unagi/config.json
+++ b/b2g/config/unagi/config.json
@@ -11,10 +11,17 @@
         "{objdir}/dist/b2g-*.tar.gz",
         "{objdir}/dist/b2g-*.crashreporter-symbols.zip",
         "{workdir}/sources.xml"
     ],
     "env": {
         "VARIANT": "user",
         "B2GUPDATER": "1"
     },
-    "gaia": {"vcs": "hgtool", "repo": "http://hg.mozilla.org/projects/gaia"}
+    "gaia": {
+        "vcs": "hgtool",
+        "repo": "http://hg.mozilla.org/projects/gaia",
+        "l10n": {
+            "vcs": "hgtool",
+            "root": "http://hg.mozilla.org/gaia-l10n"
+        }
+    }
 }
--- a/b2g/config/unagi/releng-unagi.tt
+++ b/b2g/config/unagi/releng-unagi.tt
@@ -1,8 +1,8 @@
 [
 {
-"size": 934241612,
-"digest": "acbd3f0b239998c54e0ef4eba2b01167d0c33b7a739d381e3555bab55eb1f017189fb1f54c1ad6be4937bf33c44e1409355af5d540e8a0c30414c0942e4eca29",
+"size": 805101852,
+"digest": "553e88831f0760ef8de039a037c91499ff9334691f0532835c73a44352750a4911752cf0d5346f5e023f64b2351ed7f49a2a833c27538ddde59ad505f50ab063",
 "algorithm": "sha512",
 "filename": "gonk.tar.xz"
 }
 ]
--- a/b2g/config/unagi/sources.xml
+++ b/b2g/config/unagi/sources.xml
@@ -5,105 +5,104 @@
   <remote fetch="https://android.googlesource.com/" name="aosp"/>
   <remote fetch="git://github.com/mozilla-b2g/" name="b2g"/>
   <remote fetch="git://github.com/mozilla/" name="mozilla"/>
   <remote fetch="git://codeaurora.org/" name="caf"/>
   <remote fetch="git://android.git.linaro.org/" name="linaro"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
 
   <!-- Gonk specific things and forks -->
-  <project name="platform_build" path="build" remote="b2g" revision="db539a3bd139c93c09b0cd1c3f9396b74d68717c">
+  <project name="platform_build" path="build" remote="b2g" revision="273ba23d5c6c9f6a34995a3cc429804d1449ca9f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia" path="gaia" remote="b2g" revision="ed73042e6feceb003d0114d95f1d6cfb1b3a9794"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5bb8f9eea0f68f8134b0d62a07fd3e317e139d0b"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="aafa9ca4d2e9e755fe9964018a9797eac4ecc7de"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="32106d4ea635ebe17a1610b643b398db639b8b97"/>
-  <project name="librecovery" path="librecovery" remote="b2g" revision="1355ac6c5db1777baf6ed7a9f398ea2f30f85175"/>
+  <project name="librecovery" path="librecovery" remote="b2g" revision="b7911c064a71a5c18e2c92f869f6364a798b46cd"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="6ee1f8987ef36d688f97064c003ad57849dfadf2"/>
 
   <!-- Stock Android things -->
-  <!-- Information: platform/abi/cpp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
+  <!-- Information: platform/abi/cpp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <!-- Information: platform/bionic is tagged with M8960AAAAANLYA100715A --><project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <!-- Information: platform/bootable/recovery is tagged with M8960AAAAANLYA100715A --><project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <!-- Information: platform/development is tagged with M8960AAAAANLYA100715A --><project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
-  <!-- Information: device/common is tagged with M8960AAAAANLYA10529500 --><project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
+  <!-- Information: device/common is tagged with M8960AAAAANLYA1005304 --><project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <!-- Information: device/sample is tagged with M8960AAAAANLYA100715A --><project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
-  <!-- Information: platform/external/apache-http is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/apache-http" path="external/apache-http" revision="6c9d8c58d3ed710f87c26820d903bb8aad81754f"/>
+  <!-- Information: platform/external/apache-http is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/apache-http" path="external/apache-http" revision="6c9d8c58d3ed710f87c26820d903bb8aad81754f"/>
   <!-- Information: platform/external/bluetooth/bluez is tagged with M76XXUSNEKNLYA2040 --><project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="1023c91c66e9c3bd1132480051993bf7827770f6"/>
-  <!-- Information: platform/external/bluetooth/glib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="c6b49241cc1a8950723a5f74f8f4b4f4c3fa970e"/>
-  <!-- Information: platform/external/bluetooth/hcidump is tagged with M8960AAAAANLYA10529500 --><project name="platform/external/bluetooth/hcidump" path="external/bluetooth/hcidump" revision="02b1eb24fbb3d0135a81edb4a2175b1397308d7d"/>
-  <!-- Information: platform/external/bsdiff is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/bsdiff" path="external/bsdiff" revision="81872540236d9bb15cccf963d05b9de48baa5375"/>
-  <!-- Information: platform/external/bzip2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/bzip2" path="external/bzip2" revision="048dacdca43eed1534689ececcf2781c63e1e4ba"/>
+  <!-- Information: platform/external/bluetooth/glib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="c6b49241cc1a8950723a5f74f8f4b4f4c3fa970e"/>
+  <!-- Information: platform/external/bluetooth/hcidump is tagged with M8960AAAAANLYA1005304 --><project name="platform/external/bluetooth/hcidump" path="external/bluetooth/hcidump" revision="02b1eb24fbb3d0135a81edb4a2175b1397308d7d"/>
+  <!-- Information: platform/external/bsdiff is tagged with AU_LINUX_ANDROID_JB_2.2.04.01.02.14.080 --><project name="platform/external/bsdiff" path="external/bsdiff" revision="81872540236d9bb15cccf963d05b9de48baa5375"/>
+  <!-- Information: platform/external/bzip2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/bzip2" path="external/bzip2" revision="048dacdca43eed1534689ececcf2781c63e1e4ba"/>
   <!-- Information: platform/external/dbus is tagged with M8960AAAAANLYA100715A --><project name="platform/external/dbus" path="external/dbus" revision="c7517b6195dc6926728352113e6cc335da3f9c9e"/>
   <!-- Information: platform/external/dhcpcd is tagged with M8960AAAAANLYA100715A --><project name="platform/external/dhcpcd" path="external/dhcpcd" revision="1e00fb67022d0921af0fead263f81762781b9ffa"/>
-  <!-- Information: platform/external/dnsmasq is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/dnsmasq" path="external/dnsmasq" revision="f621afad94df46204c25fc2593a19d704d2637f5"/>
-  <!-- Information: platform/external/e2fsprogs is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/e2fsprogs" path="external/e2fsprogs" revision="d5f550bb2f556c5d287f7c8d2b77223654bcec37"/>
-  <!-- Information: platform/external/expat is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/expat" path="external/expat" revision="6df134250feab71edb5915ecaa6268210bca76c5"/>
-  <!-- Information: platform/external/fdlibm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/fdlibm" path="external/fdlibm" revision="988ffeb12a6e044ae3504838ef1fee3fe0716934"/>
-  <!-- Information: platform/external/flac is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/flac" path="external/flac" revision="5893fbe890f5dab8e4146d2baa4bd2691c0739e0"/>
-  <!-- Information: platform/external/freetype is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/freetype" path="external/freetype" revision="aeb407daf3711a10a27f3bc2223c5eb05158076e"/>
-  <!-- Information: platform/external/giflib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/giflib" path="external/giflib" revision="b2597268aef084202a8c349d1cc072c03c6e22eb"/>
-  <project name="platform/external/gtest" path="external/gtest" remote="linaro" revision="85030ffa1dd68c0cc05e9115430f28627996e019"/>
-  <!-- Information: platform/external/harfbuzz is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/harfbuzz" path="external/harfbuzz" revision="116610d63a859521dacf00fb6818ee9ab2e666f6"/>
-  <!-- Information: platform/external/icu4c is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/icu4c" path="external/icu4c" revision="0fa67b93b831c6636ca18b152a1b1b14cc99b034"/>
-  <!-- Information: platform/external/iptables is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/iptables" path="external/iptables" revision="3b2deb17f065c5664bb25e1a28489e5792eb63ff"/>
-  <!-- Information: platform/external/jhead is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/jhead" path="external/jhead" revision="754078052c687f6721536009c816644c73e4f145"/>
-  <!-- Information: platform/external/jpeg is tagged with M8960AAAAANLYA10529500 --><project name="platform/external/jpeg" path="external/jpeg" revision="a62e464d672a4623233180e4023034bf825f066e"/>
-  <!-- Information: platform/external/libgsm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/libgsm" path="external/libgsm" revision="5e4516958690b9a1b2c98f88eeecba3edd2dbda4"/>
-  <!-- Information: platform/external/liblzf is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/liblzf" path="external/liblzf" revision="6946aa575b0949d045722794850896099d937cbb"/>
-  <!-- Information: platform/external/libnfc-nxp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/libnfc-nxp" path="external/libnfc-nxp" revision="3a912b065a31a72c63ad56ac224cfeaa933423b6"/>
-  <!-- Information: platform/external/libnl-headers is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/libnl-headers" path="external/libnl-headers" revision="6ccf7349d61f73ac26a0675d735d903ab919c658"/>
-  <!-- Information: platform/external/libphonenumber is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/libphonenumber" path="external/libphonenumber" revision="8d22c9a05eda1935c6dc27d188158e6ee38dc016"/>
+  <!-- Information: platform/external/dnsmasq is tagged with AU_LINUX_ANDROID_JB_2.2.04.01.02.14.080 --><project name="platform/external/dnsmasq" path="external/dnsmasq" revision="f621afad94df46204c25fc2593a19d704d2637f5"/>
+  <!-- Information: platform/external/e2fsprogs is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/e2fsprogs" path="external/e2fsprogs" revision="d5f550bb2f556c5d287f7c8d2b77223654bcec37"/>
+  <!-- Information: platform/external/expat is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/expat" path="external/expat" revision="6df134250feab71edb5915ecaa6268210bca76c5"/>
+  <!-- Information: platform/external/fdlibm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/fdlibm" path="external/fdlibm" revision="988ffeb12a6e044ae3504838ef1fee3fe0716934"/>
+  <!-- Information: platform/external/flac is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/flac" path="external/flac" revision="5893fbe890f5dab8e4146d2baa4bd2691c0739e0"/>
+  <!-- Information: platform/external/freetype is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/freetype" path="external/freetype" revision="aeb407daf3711a10a27f3bc2223c5eb05158076e"/>
+  <!-- Information: platform/external/giflib is tagged with AU_LINUX_ANDROID_JB_2.2.04.01.02.14.080 --><project name="platform/external/giflib" path="external/giflib" revision="b2597268aef084202a8c349d1cc072c03c6e22eb"/>
+  <!-- Information: platform/external/gtest is tagged with android-4.2_r1 --><project name="platform/external/gtest" path="external/gtest" remote="linaro" revision="344e5f3db17615cc853073a02968a603efd39109"/>
+  <!-- Information: platform/external/harfbuzz is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/harfbuzz" path="external/harfbuzz" revision="116610d63a859521dacf00fb6818ee9ab2e666f6"/>
+  <!-- Information: platform/external/icu4c is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/icu4c" path="external/icu4c" revision="0fa67b93b831c6636ca18b152a1b1b14cc99b034"/>
+  <!-- Information: platform/external/iptables is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/iptables" path="external/iptables" revision="3b2deb17f065c5664bb25e1a28489e5792eb63ff"/>
+  <!-- Information: platform/external/jhead is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/jhead" path="external/jhead" revision="754078052c687f6721536009c816644c73e4f145"/>
+  <!-- Information: platform/external/jpeg is tagged with M8960AAAAANLYA1005304 --><project name="platform/external/jpeg" path="external/jpeg" revision="a62e464d672a4623233180e4023034bf825f066e"/>
+  <!-- Information: platform/external/libgsm is tagged with AU_LINUX_ANDROID_JB_2.2.04.01.02.14.080 --><project name="platform/external/libgsm" path="external/libgsm" revision="5e4516958690b9a1b2c98f88eeecba3edd2dbda4"/>
+  <!-- Information: platform/external/liblzf is tagged with AU_LINUX_ANDROID_JB_2.2.04.01.02.14.080 --><project name="platform/external/liblzf" path="external/liblzf" revision="6946aa575b0949d045722794850896099d937cbb"/>
+  <!-- Information: platform/external/libnfc-nxp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/libnfc-nxp" path="external/libnfc-nxp" revision="3a912b065a31a72c63ad56ac224cfeaa933423b6"/>
+  <!-- Information: platform/external/libnl-headers is tagged with AU_LINUX_ANDROID_JB_2.2.04.01.02.14.080 --><project name="platform/external/libnl-headers" path="external/libnl-headers" revision="6ccf7349d61f73ac26a0675d735d903ab919c658"/>
+  <!-- Information: platform/external/libphonenumber is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/libphonenumber" path="external/libphonenumber" revision="8d22c9a05eda1935c6dc27d188158e6ee38dc016"/>
   <!-- Information: platform/external/libpng is tagged with M8960AAAAANLYA100715A --><project name="platform/external/libpng" path="external/libpng" revision="9c3730f0efa69f580f03463c237cd928f3196404"/>
-  <!-- Information: platform/external/libvpx is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/libvpx" path="external/libvpx" revision="3a40da0d96da5c520e7707aa14f48a80956e20d7"/>
-  <!-- Information: platform/external/llvm is tagged with M8960AAAAANLYA10529500 --><project name="platform/external/llvm" path="external/llvm" revision="bff5923831940309f7d8ddbff5826ca6ed2dc050"/>
-  <!-- Information: platform/external/mksh is tagged with M8960AAAAANLYA10529500 --><project name="platform/external/mksh" path="external/mksh" revision="ec646e8f5e7dac9a77d1de549c6ed92c04d0cd4b"/>
+  <!-- Information: platform/external/libvpx is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/libvpx" path="external/libvpx" revision="3a40da0d96da5c520e7707aa14f48a80956e20d7"/>
+  <!-- Information: platform/external/llvm is tagged with M8960AAAAANLYA1005304 --><project name="platform/external/llvm" path="external/llvm" revision="bff5923831940309f7d8ddbff5826ca6ed2dc050"/>
+  <!-- Information: platform/external/mksh is tagged with M8960AAAAANLYA1005304 --><project name="platform/external/mksh" path="external/mksh" revision="ec646e8f5e7dac9a77d1de549c6ed92c04d0cd4b"/>
   <project name="platform_external_opensans" path="external/opensans" remote="b2g" revision="b5b4c226ca1d71e936153cf679dda6d3d60e2354"/>
-  <!-- Information: platform/external/openssl is tagged with M8960AAAAANLYA100715A --><project name="platform/external/openssl" path="external/openssl" revision="27d333cce9a31c806b4bfa042925f045c727aecd"/>
-  <!-- Information: platform/external/protobuf is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/protobuf" path="external/protobuf" revision="e217977611c52bccde7f7c78e1d3c790c6357431"/>
-  <!-- Information: platform/external/safe-iop is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/safe-iop" path="external/safe-iop" revision="07073634e2e3aa4f518e36ed5dec3aabc549d5fb"/>
+  <!-- Information: platform/external/openssl is tagged with AU_LINUX_ANDROID_ICS.04.00.04.00.110 --><project name="platform/external/openssl" path="external/openssl" revision="27d333cce9a31c806b4bfa042925f045c727aecd"/>
+  <!-- Information: platform/external/protobuf is tagged with AU_LINUX_ANDROID_JB_2.2.04.01.02.14.080 --><project name="platform/external/protobuf" path="external/protobuf" revision="e217977611c52bccde7f7c78e1d3c790c6357431"/>
+  <!-- Information: platform/external/safe-iop is tagged with AU_LINUX_ANDROID_JB_2.2.04.01.02.14.080 --><project name="platform/external/safe-iop" path="external/safe-iop" revision="07073634e2e3aa4f518e36ed5dec3aabc549d5fb"/>
   <project name="screencap-gonk" path="external/screencap-gonk" remote="b2g" revision="e6403c71e9eca8cb943739d5a0a192deac60fc51"/>
   <!-- Information: platform/external/skia is tagged with M8960AAAAANLYA100715A --><project name="platform/external/skia" path="external/skia" revision="7d90c85f2c0e3b747f7c7eff8bc9253b0063b439"/>
-  <!-- Information: platform/external/sonivox is tagged with M8960AAAAANLYA10529500 --><project name="platform/external/sonivox" path="external/sonivox" revision="7c967779dfc61ac1f346e972de91d4bfce7dccbb"/>
-  <!-- Information: platform/external/speex is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/speex" path="external/speex" revision="ebe6230a7f7c69f5a4389f2b09b7b19ef9e94f32"/>
+  <!-- Information: platform/external/sonivox is tagged with M8960AAAAANLYA1005304 --><project name="platform/external/sonivox" path="external/sonivox" revision="7c967779dfc61ac1f346e972de91d4bfce7dccbb"/>
+  <!-- Information: platform/external/speex is tagged with AU_LINUX_ANDROID_JB_2.2.04.01.02.14.080 --><project name="platform/external/speex" path="external/speex" revision="ebe6230a7f7c69f5a4389f2b09b7b19ef9e94f32"/>
   <project name="platform/external/sqlite" path="external/sqlite" revision="fb30e613139b8836fdc8e81e166cf3a76e5fa17f"/>
-  <!-- Information: platform/external/stlport is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/stlport" path="external/stlport" revision="a6734e0645fce81c9610de0488b729207bfa576e"/>
-  <!-- Information: platform/external/strace is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/strace" path="external/strace" revision="c9fd2e5ef7d002e12e7cf2512506c84a9414b0fd"/>
-  <!-- Information: platform/external/tagsoup is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/tagsoup" path="external/tagsoup" revision="68c2ec9e0acdb3214b7fb91dbab8c9fab8736817"/>
-  <!-- Information: platform/external/tinyalsa is tagged with M8960AAAAANLYA10529500 --><project name="platform/external/tinyalsa" path="external/tinyalsa" revision="06cc244ee512c1352215e543615738bc8ac82814"/>
-  <!-- Information: platform/external/tremolo is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/tremolo" path="external/tremolo" revision="25bd78d2392dbdc879ae53382cde9d019f79cf6f"/>
+  <!-- Information: platform/external/stlport is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/stlport" path="external/stlport" revision="a6734e0645fce81c9610de0488b729207bfa576e"/>
+  <!-- Information: platform/external/strace is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/strace" path="external/strace" revision="c9fd2e5ef7d002e12e7cf2512506c84a9414b0fd"/>
+  <!-- Information: platform/external/tagsoup is tagged with AU_LINUX_ANDROID_JB_2.2.04.01.02.14.080 --><project name="platform/external/tagsoup" path="external/tagsoup" revision="68c2ec9e0acdb3214b7fb91dbab8c9fab8736817"/>
+  <!-- Information: platform/external/tinyalsa is tagged with M8960AAAAANLYA1005304 --><project name="platform/external/tinyalsa" path="external/tinyalsa" revision="06cc244ee512c1352215e543615738bc8ac82814"/>
+  <!-- Information: platform/external/tremolo is tagged with AU_LINUX_ANDROID_JB_2.2.04.01.02.14.080 --><project name="platform/external/tremolo" path="external/tremolo" revision="25bd78d2392dbdc879ae53382cde9d019f79cf6f"/>
   <project name="unbootimg" path="external/unbootimg" remote="b2g" revision="9464623d92eb8668544916dc5a8f4f6337d0bc08"/>
-  <!-- Information: platform/external/webp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/webp" path="external/webp" revision="88fe2b83c4b9232cd08729556fd0485d6a6a92cd"/>
-  <!-- Information: platform/external/webrtc is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/webrtc" path="external/webrtc" revision="137024dc8a2e9251a471e20518a9c3ae06f81f23"/>
-  <!-- Information: platform/external/wpa_supplicant is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/wpa_supplicant" path="external/wpa_supplicant" revision="a01d37870bbf9892d43e792e5de0683ca41c5497"/>
+  <!-- Information: platform/external/webp is tagged with AU_LINUX_ANDROID_JB_2.2.04.01.02.14.080 --><project name="platform/external/webp" path="external/webp" revision="88fe2b83c4b9232cd08729556fd0485d6a6a92cd"/>
+  <!-- Information: platform/external/webrtc is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/webrtc" path="external/webrtc" revision="137024dc8a2e9251a471e20518a9c3ae06f81f23"/>
+  <!-- Information: platform/external/wpa_supplicant is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/wpa_supplicant" path="external/wpa_supplicant" revision="a01d37870bbf9892d43e792e5de0683ca41c5497"/>
   <!-- Information: platform/external/hostap is tagged with M8960AAAAANLYA1047 --><project name="platform/external/hostap" path="external/hostap" revision="bf04b0faadbdeb4b7943f2e2c4c5aa59df872bb1"/>
-  <!-- Information: platform/external/zlib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/zlib" path="external/zlib" revision="f96a1d1ebfdf1cd582210fd09c23d8f59e0ae094"/>
-  <!-- Information: platform/external/yaffs2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/external/yaffs2" path="external/yaffs2" revision="0afa916204c664b3114429637b63af1321a0aeca"/>
+  <!-- Information: platform/external/zlib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/zlib" path="external/zlib" revision="f96a1d1ebfdf1cd582210fd09c23d8f59e0ae094"/>
+  <!-- Information: platform/external/yaffs2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/external/yaffs2" path="external/yaffs2" revision="0afa916204c664b3114429637b63af1321a0aeca"/>
   <!-- Information: platform/frameworks/base is tagged with M76XXUSNEKNLYA2040 --><project name="platform/frameworks/base" path="frameworks/base" revision="eb2bc75803ca179353c24c364a9c8a8ce23e8b78"/>
-  <!-- Information: platform/frameworks/opt/emoji is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="a95d8db002770469d72dfaf59ff37ac96db29a87"/>
-  <!-- Information: platform/frameworks/support is tagged with M8960AAAAANLYA10529500 --><project name="platform/frameworks/support" path="frameworks/support" revision="27208692b001981f1806f4f396434f4eac78b909"/>
+  <!-- Information: platform/frameworks/opt/emoji is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="a95d8db002770469d72dfaf59ff37ac96db29a87"/>
+  <!-- Information: platform/frameworks/support is tagged with M8960AAAAANLYA1005304 --><project name="platform/frameworks/support" path="frameworks/support" revision="27208692b001981f1806f4f396434f4eac78b909"/>
   <!-- Information: platform/hardware/libhardware is tagged with M8960AAAAANLYA1049B --><project name="platform/hardware/libhardware" path="hardware/libhardware" revision="4a619901847621f8a7305edf42dd07347a140484"/>
   <!-- Information: platform/hardware/libhardware_legacy is tagged with M8960AAAAANLYA153611 --><project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="87b4d7afa8f854b445e2d0d95091f6f6069f2b30"/>
   <!-- Information: platform/libcore is tagged with M8960AAAAANLYA100715A --><project name="platform/libcore" path="libcore" revision="30841f9fba9ccd5c54f4f079f495994db97f283e"/>
-  <!-- Information: platform/ndk is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/ndk" path="ndk" revision="9f555971e1481854d5b4dc11b3e6af9fff4f241f"/>
-  <!-- Information: platform/prebuilt is tagged with M8960AAAAANLYA10529500 --><project name="platform/prebuilt" path="prebuilt" revision="447ea790fcc957dde59730ecc2a65ca263bdc733"/>
+  <!-- Information: platform/ndk is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/ndk" path="ndk" revision="9f555971e1481854d5b4dc11b3e6af9fff4f241f"/>
+  <!-- Information: platform/prebuilt is tagged with M8960AAAAANLYA1005304 --><project name="platform/prebuilt" path="prebuilt" revision="447ea790fcc957dde59730ecc2a65ca263bdc733"/>
   <!-- Information: platform/system/bluetooth is tagged with M8960AAAAANLYA100703 --><project name="platform/system/bluetooth" path="system/bluetooth" revision="7772cad4823f1f427ce1d4df84a55982386d6d18"/>
   <!-- Information: platform/system/core is tagged with M76XXUSNEKNLYA2040 --><project name="platform/system/core" path="system/core" revision="bf1970408676ce570b8f4dc3efa038e47552137f"/>
-  <!-- Information: platform/system/extras is tagged with M8960AAAAANLYA10529500 --><project name="platform/system/extras" path="system/extras" revision="01db6c1254e1407740a543f24317fc540fc4c049"/>
-  <!-- Information: platform/system/media is tagged with M8960AAAAANLYA10529500 --><project name="platform/system/media" path="system/media" revision="7f71c7fd362bbd992ff2e0e80f7af5859ad116ad"/>
+  <!-- Information: platform/system/extras is tagged with M8960AAAAANLYA1005304 --><project name="platform/system/extras" path="system/extras" revision="01db6c1254e1407740a543f24317fc540fc4c049"/>
+  <!-- Information: platform/system/media is tagged with M8960AAAAANLYA1005304 --><project name="platform/system/media" path="system/media" revision="7f71c7fd362bbd992ff2e0e80f7af5859ad116ad"/>
   <!-- Information: platform/system/netd is tagged with M8960AAAAANLYA1049 --><project name="platform/system/netd" path="system/netd" revision="306e765248e3900041bf2737e9f57b1b5694a4ce"/>
   <!-- Information: platform/system/vold is tagged with M8960AAAAANLYA100715A --><project name="platform/system/vold" path="system/vold" revision="99fff257d53cc045d1460841edca5d901dacfcf5"/>
 
   <!-- Otoro/Unagi specific things -->
   <!-- Information: device/qcom/common is tagged with M8960AAAAANLYA100715A --><project name="device/qcom/common" path="device/qcom/common" revision="b9cdab8e1e1a215a8c65b8d5816f666bec7be205"/>
   <!-- Information: platform/vendor/qcom/msm7627a is tagged with M8960AAAAANLYA100715A --><project name="platform/vendor/qcom/msm7627a" path="device/qcom/msm7627a" revision="d920a502ba17cf4d716f8b1a615f07e796b0501a"/>
-  <project name="android-device-otoro" path="device/qcom/otoro" remote="b2g" revision="285594a1e23170de6adba58c278a01e9a4d13fe1"/>
-  <project name="android-device-unagi" path="device/qcom/unagi" remote="b2g" revision="3582f3be9e2658cdc2d9073b34ba2f5032c143d3"/>
+  <project name="android-device-otoro" path="device/qcom/otoro" remote="b2g" revision="ad559059650d98bb7049720086401fc7ed4d24fb"/>
+  <project name="android-device-unagi" path="device/qcom/unagi" remote="b2g" revision="c83d823b231b6ad8f48ba486375a0c951a4f42fb"/>
   <project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="0a01247e4b0880f93424b27251cd3a1f6b19dbb2"/>
   <!-- Information: platform/hardware/qcom/camera is tagged with M76XXUSNEKNLYA2040 --><project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="1acf77a75e30f3fc8b1eed2057c97adf1cb1633f"/>
   <project name="hardware_qcom_display" path="hardware/qcom/display" remote="b2g" revision="6405d30f2fac7d8a1f2cb17b99fb7dd0a8bcfdac"/>
   <!-- Information: platform/hardware/qcom/media is tagged with M8960AAAAANLYA100715A --><project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="552c3ddb7174a01f3508782d40c4d8c845ab441a"/>
   <!-- Information: platform/hardware/qcom/gps is tagged with M8960AAAAANLYA100705 --><project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="23d5707b320d7fc69f8ba3b7d84d78a1c5681708"/>
-  <!-- Information: platform/hardware/msm7k is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.079 --><project name="platform/hardware/msm7k" path="hardware/msm7k" revision="8892d46805c5639b55dd07547745c5180da861e7"/>
+  <!-- Information: platform/hardware/msm7k is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.111 --><project name="platform/hardware/msm7k" path="hardware/msm7k" revision="8892d46805c5639b55dd07547745c5180da861e7"/>
   <!-- Information: platform/vendor/qcom-opensource/omx/mm-core is tagged with M8960AAAAANLYA100715A --><project name="platform/vendor/qcom-opensource/omx/mm-core" path="vendor/qcom/opensource/omx/mm-core" revision="ab17ac9a074b4bb69986a8436336bdfbbaf9cd39"/>
   <!-- Information: platform/hardware/ril is tagged with M76XXUSNEKNLYA1610 --><project name="platform/hardware/ril" path="hardware/ril" remote="caf" revision="fe9a3f63922143b57e79ed570bab2328df8c83a5"/>
 </manifest>
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -49,16 +49,17 @@
 #ifndef MOZ_STATIC_JS
 @BINPATH@/@DLL_PREFIX@mozjs@DLL_SUFFIX@
 #endif
 @BINPATH@/@DLL_PREFIX@plc4@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@plds4@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@xpcom@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@nspr4@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@mozalloc@DLL_SUFFIX@
+@BINPATH@/@DLL_PREFIX@soundtouch@DLL_SUFFIX@
 #ifdef XP_MACOSX
 @BINPATH@/XUL
 #else
 @BINPATH@/@DLL_PREFIX@xul@DLL_SUFFIX@
 #endif
 #ifdef XP_MACOSX
 @BINPATH@/@MOZ_CHILD_PROCESS_NAME@.app/
 #else
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -57,19 +57,18 @@ XPCOMUtils.defineLazyGetter(window, "gFi
 
   // Force a style flush to ensure that our binding is attached.
   findbar.clientTop;
   findbar.browser = gBrowser;
   window.gFindBarInitialized = true;
   return findbar;
 });
 
-__defineGetter__("gPrefService", function() {
-  delete this.gPrefService;
-  return this.gPrefService = Services.prefs;
+XPCOMUtils.defineLazyGetter(this, "gPrefService", function() {
+  return Services.prefs;
 });
 
 __defineGetter__("AddonManager", function() {
   let tmp = {};
   Cu.import("resource://gre/modules/AddonManager.jsm", tmp);
   return this.AddonManager = tmp.AddonManager;
 });
 __defineSetter__("AddonManager", function (val) {
@@ -82,27 +81,24 @@ XPCOMUtils.defineLazyGetter(window, "gFi
   return this.PluralForm;
 });
 __defineSetter__("PluralForm", function (val) {
   delete this.PluralForm;
   return this.PluralForm = val;
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
-                                  "resource://gre/modules/TelemetryStopwatch.jsm");
+  "resource://gre/modules/TelemetryStopwatch.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AboutHomeUtils",
-                                  "resource:///modules/AboutHomeUtils.jsm");
+  "resource:///modules/AboutHomeUtils.jsm");
 
 #ifdef MOZ_SERVICES_SYNC
-XPCOMUtils.defineLazyGetter(this, "Weave", function() {
-  let tmp = {};
-  Cu.import("resource://services-sync/main.js", tmp);
-  return tmp.Weave;
-});
+XPCOMUtils.defineLazyModuleGetter(this, "Weave",
+  "resource://services-sync/main.js");
 #endif
 
 XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () {
   let tmp = {};
   Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
   try {
     return new tmp.PopupNotifications(gBrowser,
                                       document.getElementById("notification-popup"),
@@ -131,31 +127,25 @@ XPCOMUtils.defineLazyGetter(this, "Debug
 });
 
 XPCOMUtils.defineLazyGetter(this, "Tilt", function() {
   let tmp = {};
   Cu.import("resource:///modules/devtools/Tilt.jsm", tmp);
   return new tmp.Tilt(window);
 });
 
-XPCOMUtils.defineLazyGetter(this, "Social", function() {
-  let tmp = {};
-  Cu.import("resource:///modules/Social.jsm", tmp);
-  return tmp.Social;
-});
+XPCOMUtils.defineLazyModuleGetter(this, "Social",
+  "resource:///modules/Social.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
   "resource:///modules/PageThumbs.jsm");
 
 #ifdef MOZ_SAFE_BROWSING
-XPCOMUtils.defineLazyGetter(this, "SafeBrowsing", function() {
-  let tmp = {};
-  Cu.import("resource://gre/modules/SafeBrowsing.jsm", tmp);
-  return tmp.SafeBrowsing;
-});
+XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
+  "resource://gre/modules/SafeBrowsing.jsm");
 #endif
 
 XPCOMUtils.defineLazyModuleGetter(this, "gBrowserNewTabPreloader",
   "resource:///modules/BrowserNewTabPreloader.jsm", "BrowserNewTabPreloader");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -153,17 +153,16 @@ endif
                  browser_bug655584.js \
                  browser_bug664672.js \
                  browser_bug710878.js \
                  browser_bug719271.js \
                  browser_bug724239.js \
                  browser_bug735471.js \
                  browser_bug743421.js \
                  browser_bug749738.js \
-                 browser_bug763468.js \
                  browser_bug767836.js \
                  browser_bug783614.js \
                  browser_bug797677.js \
                  browser_canonizeURL.js \
                  browser_customize.js \
                  browser_findbarClose.js \
                  browser_homeDrop.js \
                  browser_keywordBookmarklets.js \
@@ -298,21 +297,23 @@ else
 
 # TODO: Activate after carbon test plugin lands, bug 628651
 # 		browser_maconly_carbon_mismatch_plugin.js \
 
 endif
 
 ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
 _BROWSER_FILES += \
+                browser_bug763468_perwindowpb.js \
                 browser_private_browsing_window.js \
                 $(filter disabled-until-bug-722850, browser_save_link-perwindowpb.js) \
                 $(NULL)
 else
 _BROWSER_FILES += \
+                browser_bug763468.js \
                 browser_save_link.js \
                 $(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
copy from browser/base/content/test/browser_bug763468.js
copy to browser/base/content/test/browser_bug763468_perwindowpb.js
--- a/browser/base/content/test/browser_bug763468.js
+++ b/browser/base/content/test/browser_bug763468_perwindowpb.js
@@ -1,81 +1,66 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This test makes sure that opening a new tab in private browsing mode opens about:privatebrowsing
-
-// initialization
-const pb = Cc["@mozilla.org/privatebrowsing;1"].
-           getService(Ci.nsIPrivateBrowsingService);
-
-const PREF = "browser.newtab.url";
-
 function test() {
-  let newTabUrl = Services.prefs.getCharPref(PREF) || "about:blank";
+  // initialization
+  waitForExplicitFinish();
+  let windowsToClose = [];
+  let newTab;
+  let newTabPrefName = "browser.newtab.url";
+  let newTabURL;
+  let mode;
 
-  waitForExplicitFinish();
-  // check whether the mode that we start off with is normal or not
-  ok(!pb.privateBrowsingEnabled, "private browsing is disabled");
+  function doTest(aIsPrivateMode, aWindow, aCallback) {
+    aWindow.BrowserOpenTab();
+    aWindow.gBrowser.selectedTab.linkedBrowser.addEventListener("load", function onLoad() {
+      aWindow.gBrowser.selectedTab.linkedBrowser.removeEventListener("load", onLoad, true);
+      if (aIsPrivateMode) {
+        mode = "per window private browsing";
+        newTabURL = "about:privatebrowsing";
+      } else {
+        mode = "normal";
+        newTabURL = Services.prefs.getCharPref(newTabPrefName) || "about:blank";
+      }
 
-  // Open a new tab page in normal mode
-  openNewTab(function () {
-    // Check the new tab opened while in normal mode
-    is(gBrowser.selectedBrowser.currentURI.spec, newTabUrl,
-       "URL of NewTab should be browser.newtab.url in Normal mode");
+      is(aWindow.gBrowser.currentURI.spec, newTabURL,
+        "URL of NewTab should be " + newTabURL + " in " + mode +  " mode");
 
-    // enter private browsing mode
-    togglePrivateBrowsing(function () {
-      ok(pb.privateBrowsingEnabled, "private browsing is enabled");
-    
-      // Open a new tab page in private browsing mode
-      openNewTab(function () {
-        // Check the new tab opened while in private browsing mode
-        is(gBrowser.selectedBrowser.currentURI.spec, "about:privatebrowsing",
-           "URL of NewTab should be about:privatebrowsing in PB mode");
+      aWindow.gBrowser.removeTab(aWindow.gBrowser.selectedTab);
+      aCallback()
+    }, true);
+  };
+
+  function testOnWindow(aOptions, aCallback) {
+    whenNewWindowLoaded(aOptions, function(aWin) {
+      windowsToClose.push(aWin);
+      // execute should only be called when need, like when you are opening
+      // web pages on the test. If calling executeSoon() is not necesary, then
+      // call whenNewWindowLoaded() instead of testOnWindow() on your test.
+      executeSoon(function() aCallback(aWin));
+    });
+  };
 
-        // exit private browsing mode
-        togglePrivateBrowsing(function () {
-          ok(!pb.privateBrowsingEnabled, "private browsing is disabled");
-          
-          // Open a new tab page in normal mode to check if
-          // returning from pb mode restores everything as it should
-          openNewTab(function () {
-            // Check the new tab opened while in normal mode
-            is(gBrowser.selectedBrowser.currentURI.spec, newTabUrl, 
-               "URL of NewTab should be browser.newtab.url in Normal mode");
-            gBrowser.removeTab(gBrowser.selectedTab);
-            gBrowser.removeTab(gBrowser.selectedTab);
-            finish();
+   // this function is called after calling finish() on the test.
+  registerCleanupFunction(function() {
+    windowsToClose.forEach(function(aWin) {
+      aWin.close();
+    });
+  });
+
+  // test first when not on private mode
+  testOnWindow({}, function(aWin) {
+    doTest(false, aWin, function() {
+      // then test when on private mode
+      testOnWindow({private: true}, function(aWin) {
+        doTest(true, aWin, function() {
+          // then test again when not on private mode
+          testOnWindow({}, function(aWin) {
+            doTest(false, aWin, finish);
           });
         });
       });
     });
   });
 }
-
-function togglePrivateBrowsing(aCallback) {
-  let topic = "private-browsing-transition-complete";
-
-  Services.obs.addObserver(function observe() {
-    Services.obs.removeObserver(observe, topic);
-    executeSoon(aCallback);
-  }, topic, false);
-
-  pb.privateBrowsingEnabled = !pb.privateBrowsingEnabled;
-}
-
-function openNewTab(aCallback) {
-  // Open a new tab
-  BrowserOpenTab();
-
-  let browser = gBrowser.selectedBrowser;
-  if (browser.contentDocument.readyState == "complete") {
-    executeSoon(aCallback);
-    return;
-  }
-
-  browser.addEventListener("load", function onLoad() {
-    browser.removeEventListener("load", onLoad, true);
-    executeSoon(aCallback);
-  }, true);
-}
--- a/browser/base/content/test/head.js
+++ b/browser/base/content/test/head.js
@@ -195,8 +195,16 @@ function runSocialTests(tests, cbPreTest
           ok(false, "sub-test " + name + " failed: " + ex.toString() +"\n"+ex.stack);
           cleanupAndRunNextTest();
         }
       })
     });
   }
   runNextTest();
 }
+
+function whenNewWindowLoaded(aOptions, aCallback) {
+  let win = OpenBrowserWindow(aOptions);
+  win.addEventListener("load", function onLoad() {
+    win.removeEventListener("load", onLoad, false);
+    aCallback(win);
+  }, false);
+}
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -778,16 +778,22 @@ const DownloadsView = {
       return;
     }
 
     switch (aEvent.keyCode) {
       case KeyEvent.DOM_VK_ENTER:
       case KeyEvent.DOM_VK_RETURN:
         goDoCommand("downloadsCmd_doDefault");
         break;
+      case KeyEvent.DOM_VK_DOWN:
+        // Are we focused on the last element in the list?
+        if (this.richListBox.currentIndex == (this.richListBox.itemCount - 1)) {
+          DownloadsFooter.focus();
+        }
+        break;
     }
   },
 
   onDownloadContextMenu: function DV_onDownloadContextMenu(aEvent)
   {
     let element = this.richListBox.selectedItem;
     if (!element) {
       return;
@@ -1424,28 +1430,37 @@ const DownloadsSummary = {
    * unsubscribes from the DownloadsCommon DownloadsSummaryData singleton.
    *
    * @param aVisible
    *        True if the summary should be shown.
    */
   set visible(aVisible)
   {
     if (aVisible == this._visible || !this._summaryNode) {
-      return;
+      return this._visible;
     }
     if (aVisible) {
       DownloadsCommon.getSummary(DownloadsView.kItemCountLimit)
                      .addView(this);
     } else {
       DownloadsCommon.getSummary(DownloadsView.kItemCountLimit)
                      .removeView(this);
     }
     this._summaryNode.collapsed = !aVisible;
     return this._visible = aVisible;
   },
+
+  /**
+   * Returns the collapsed state of the downloads summary.
+   */
+  get visible()
+  {
+    return this._visible;
+  },
+
   _visible: false,
 
   /**
    * Sets whether or not we show the progress bar.
    *
    * @param aShowingProgress
    *        True if we should show the progress bar.
    */
@@ -1502,16 +1517,52 @@ const DownloadsSummary = {
     if (this._detailsNode) {
       this._detailsNode.setAttribute("value", aValue);
       this._detailsNode.setAttribute("tooltiptext", aValue);
     }
     return aValue;
   },
 
   /**
+   * Focuses the root element of the summary.
+   */
+  focus: function()
+  {
+    if (this._summaryNode) {
+      this._summaryNode.focus();
+    }
+  },
+
+  /**
+   * Respond to keypress events on the Downloads Summary node.
+   *
+   * @param aEvent
+   *        The keypress event being handled.
+   */
+  onKeyPress: function DS_onKeyPress(aEvent)
+  {
+    if (aEvent.charCode == " ".charCodeAt(0) ||
+        aEvent.keyCode == KeyEvent.DOM_VK_ENTER ||
+        aEvent.keyCode == KeyEvent.DOM_VK_RETURN) {
+      DownloadsPanel.showDownloadsHistory();
+    }
+  },
+
+  /**
+   * Respond to click events on the Downloads Summary node.
+   *
+   * @param aEvent
+   *        The click event being handled.
+   */
+  onClick: function DS_onClick(aEvent)
+  {
+    DownloadsPanel.showDownloadsHistory();
+  },
+
+  /**
    * Element corresponding to the root of the downloads summary.
    */
   get _summaryNode()
   {
     let node = document.getElementById("downloadsSummary");
     if (!node) {
       return null;
     }
@@ -1555,8 +1606,47 @@ const DownloadsSummary = {
     let node = document.getElementById("downloadsSummaryDetails");
     if (!node) {
       return null;
     }
     delete this._detailsNode;
     return this._detailsNode = node;
   }
 }
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsFooter
+
+/**
+ * Manages events sent to to the footer vbox, which contains both the
+ * DownloadsSummary as well as the "Show All Downloads" button.
+ */
+const DownloadsFooter = {
+
+  /**
+   * Focuses the appropriate element within the footer. If the summary
+   * is visible, focus it. If not, focus the "Show All Downloads"
+   * button.
+   */
+  focus: function DF_focus()
+  {
+    if (DownloadsSummary.visible) {
+      DownloadsSummary.focus();
+    } else {
+      DownloadsView.downloadsHistory.focus();
+    }
+  },
+
+  /**
+   * Handles keypress events on the footer element.
+   */
+  onKeyPress: function DF_onKeyPress(aEvent)
+  {
+    // If the up key is pressed, and the downloads list has at least 1 element
+    // in it, focus the last element in the list.
+    if (aEvent.keyCode == KeyEvent.DOM_VK_UP &&
+        DownloadsView.richListBox.itemCount > 0) {
+      DownloadsView.richListBox.focus();
+      DownloadsView.richListBox.selectedIndex =
+        (DownloadsView.richListBox.itemCount - 1);
+    }
+  }
+};
--- a/browser/components/downloads/content/downloadsOverlay.xul
+++ b/browser/components/downloads/content/downloadsOverlay.xul
@@ -100,38 +100,42 @@
       <richlistbox id="downloadsListBox"
                    class="plain"
                    flex="1"
                    context="downloadsContextMenu"
                    onkeypress="DownloadsView.onDownloadKeyPress(event);"
                    oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
                    ondragstart="DownloadsView.onDownloadDragStart(event);"/>
 
-      <hbox id="downloadsSummary"
-            collapsed="true"
-            align="center"
-            orient="horizontal"
-            onclick="DownloadsPanel.showDownloadsHistory();">
-        <image class="downloadTypeIcon" />
-        <vbox>
-          <description id="downloadsSummaryDescription"
-                       class="downloadTarget"
-                       style="min-width: &downloadsSummary.minWidth;"/>
-          <progressmeter id="downloadsSummaryProgress"
-                         class="downloadProgress"
-                         min="0"
-                         max="100"
-                         mode="normal" />
-          <description id="downloadsSummaryDetails"
-                       class="downloadDetails"
-                       style="width: &downloadDetails.width;"
-                       crop="end"/>
-        </vbox>
-      </hbox>
+      <vbox id="downloadsFooter"
+            onkeypress="DownloadsFooter.onKeyPress(event);">
+        <hbox id="downloadsSummary"
+              collapsed="true"
+              align="center"
+              orient="horizontal"
+              onkeypress="DownloadsSummary.onKeyPress(event);"
+              onclick="DownloadsSummary.onClick(event);">
+          <image class="downloadTypeIcon" />
+          <vbox>
+            <description id="downloadsSummaryDescription"
+                         class="downloadTarget"
+                         style="min-width: &downloadsSummary.minWidth;"/>
+            <progressmeter id="downloadsSummaryProgress"
+                           class="downloadProgress"
+                           min="0"
+                           max="100"
+                           mode="normal" />
+            <description id="downloadsSummaryDetails"
+                         class="downloadDetails"
+                         style="width: &downloadDetails.width;"
+                         crop="end"/>
+          </vbox>
+        </hbox>
 
-      <button id="downloadsHistory"
-              class="plain"
-              label="&downloadsHistory.label;"
-              accesskey="&downloadsHistory.accesskey;"
-              oncommand="DownloadsPanel.showDownloadsHistory();"/>
+        <button id="downloadsHistory"
+                class="plain"
+                label="&downloadsHistory.label;"
+                accesskey="&downloadsHistory.accesskey;"
+                oncommand="DownloadsPanel.showDownloadsHistory();"/>
+      </vbox>
     </panel>
   </popupset>
 </overlay>
--- a/browser/components/downloads/src/DownloadsCommon.jsm
+++ b/browser/components/downloads/src/DownloadsCommon.jsm
@@ -748,17 +748,31 @@ const DownloadsData = {
     }
 
     dataItem.state = aDownload.state;
     dataItem.referrer = aDownload.referrer && aDownload.referrer.spec;
     dataItem.resumable = aDownload.resumable;
     dataItem.startTime = Math.round(aDownload.startTime / 1000);
     dataItem.currBytes = aDownload.amountTransferred;
     dataItem.maxBytes = aDownload.size;
-    dataItem.download = aDownload;
+
+    // When a download is retried, we create a different download object from
+    // the database with the same ID as before. This means that the nsIDownload
+    // that the dataItem holds might now need updating.
+    //
+    // It's possible, however, that dataItem.download is still a lazy getter
+    // if we never read the download property after initializing the dataItem
+    // from a data row in the downloads database. In that case, we can leave
+    // it alone - the next time the download property is accessed, the right
+    // download object will be retrieved.
+    let downloadIsGetter = Object.getOwnPropertyDescriptor(dataItem, "download")
+                                 .value === undefined;
+    if (!downloadIsGetter) {
+      dataItem.download = aDownload;
+    }
 
     this._views.forEach(
       function (view) view.getViewItem(dataItem).onStateChange()
     );
 
     if (isNew && !dataItem.newDownloadNotified) {
       dataItem.newDownloadNotified = true;
       this._notifyNewDownload();
@@ -820,17 +834,16 @@ const DownloadsData = {
     }
 
     // Show the panel in the most recent browser window, if present.
     let browserWin = gBrowserGlue.getMostRecentBrowserWindow();
     if (!browserWin) {
       return;
     }
 
-    browserWin.focus();
     if (this.panelHasShownBefore) {
       // For new downloads after the first one, don't show the panel
       // automatically, but provide a visible notification in the topmost
       // browser window, if the status indicator is already visible.
       browserWin.DownloadsIndicatorView.showEventNotification();
       return;
     }
     this.panelHasShownBefore = true;
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -511,18 +511,27 @@ nsBrowserContentHandler.prototype = {
       }
     }
     if (cmdLine.handleFlag("preferences", false)) {
       openPreferences();
       cmdLine.preventDefault = true;
     }
     if (cmdLine.handleFlag("silent", false))
       cmdLine.preventDefault = true;
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    if (cmdLine.findFlag("private-window", false) >= 0) {
+      openWindow(null, this.chromeURL, "_blank",
+        "chrome,dialog=no,private,all" + this.getFeatures(cmdLine),
+        "about:privatebrowsing");
+      cmdLine.preventDefault = true;
+    }
+#else
     if (cmdLine.findFlag("private-toggle", false) >= 0)
       cmdLine.preventDefault = true;
+#endif
 
     var searchParam = cmdLine.handleFlagWithParam("search", false);
     if (searchParam) {
       doSearch(searchParam, cmdLine);
       cmdLine.preventDefault = true;
     }
 
     // The global PB Service consumes this flag, so only eat it in per-window
--- a/browser/components/preferences/in-content/tests/Makefile.in
+++ b/browser/components/preferences/in-content/tests/Makefile.in
@@ -19,18 +19,20 @@ include $(topsrcdir)/config/rules.mk
     browser_bug731866.js \
     browser_connection.js \
     privacypane_tests.js \
     browser_privacypane_1.js \
     browser_privacypane_2.js \
     browser_privacypane_3.js \
     browser_privacypane_4.js \
     browser_privacypane_5.js \
-    browser_privacypane_6.js \
     browser_privacypane_8.js \
     $(NULL)
 
 ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
-_BROWSER_FILES += browser_privacypane_7.js
+_BROWSER_FILES += \
+    browser_privacypane_6.js \
+    browser_privacypane_7.js \
+    $(NULL)
 endif
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/components/preferences/tests/Makefile.in
+++ b/browser/components/preferences/tests/Makefile.in
@@ -17,20 +17,22 @@ include $(topsrcdir)/config/rules.mk
     browser_bug567487.js \
     browser_bug705422.js \
     privacypane_tests.js \
     browser_privacypane_1.js \
     browser_privacypane_2.js \
     browser_privacypane_3.js \
     browser_privacypane_4.js \
     browser_privacypane_5.js \
-    browser_privacypane_6.js \
     browser_privacypane_8.js \
     browser_permissions.js \
     browser_chunk_permissions.js \
     $(NULL)
 
 ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
-_BROWSER_FILES += browser_privacypane_7.js
+_BROWSER_FILES += \
+    browser_privacypane_6.js \
+    browser_privacypane_7.js \
+    $(NULL)
 endif
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/components/privatebrowsing/test/browser/perwindow/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/perwindow/Makefile.in
@@ -33,17 +33,19 @@ MOCHITEST_BROWSER_FILES =  \
 		browser_privatebrowsing_opendir.js \
 		browser_privatebrowsing_openlocation.js \
 		browser_privatebrowsing_openLocationLastURL.js \
 		browser_privatebrowsing_placestitle.js \
 		browser_privatebrowsing_popupblocker.js \
 		browser_privatebrowsing_protocolhandler.js \
 		browser_privatebrowsing_protocolhandler_page.html \
 		browser_privatebrowsing_theming.js \
+		browser_privatebrowsing_ui.js \
 		browser_privatebrowsing_urlbarfocus.js \
 		browser_privatebrowsing_windowtitle.js \
 		browser_privatebrowsing_windowtitle_page.html \
+		browser_privatebrowsing_zoom.js \
 		browser_privatebrowsing_zoomrestore.js \
 		popup.html \
 		title.sjs \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
copy from browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_ui.js
copy to browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_ui.js
--- a/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_ui.js
+++ b/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_ui.js
@@ -3,100 +3,80 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This test makes sure that the gPrivateBrowsingUI object, the Private Browsing
 // menu item and its XUL <command> element work correctly.
 
 function test() {
   // initialization
   waitForExplicitFinish();
-  gPrefService.setBoolPref("browser.privatebrowsing.keep_current_session", true);
-  let pb = Cc["@mozilla.org/privatebrowsing;1"].
-           getService(Ci.nsIPrivateBrowsingService);
-  let observerData;
-  function observer(aSubject, aTopic, aData) {
-    if (aTopic == "private-browsing")
-      observerData = aData;
-  }
-  Services.obs.addObserver(observer, "private-browsing", false);
-  let pbMenuItem = document.getElementById("privateBrowsingItem");
-  // add a new blank tab to ensure the title can be meaningfully compared later
-  gBrowser.selectedTab = gBrowser.addTab();
-  let originalTitle = document.title;
+  let windowsToClose = [];
+  let testURI = "about:blank";
+  let pbMenuItem;
+  let cmd;
+
+  function doTest(aIsPrivateMode, aWindow, aCallback) {
+    aWindow.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+      aWindow.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+
+      ok(aWindow.gPrivateBrowsingUI, "The gPrivateBrowsingUI object exists");
+
+      pbMenuItem = aWindow.document.getElementById("menu_newPrivateWindow");
+      ok(pbMenuItem, "The Private Browsing menu item exists");
 
-  function testNewWindow(aCallback, expected) {
-    Services.obs.addObserver(function observer1(aSubject, aTopic, aData) {
+      cmd = aWindow.document.getElementById("Tools:PrivateBrowsing");
+      isnot(cmd, null, "XUL command object for the private browsing service exists");
+
+      is(pbMenuItem.getAttribute("label"), "New Private Window",
+        "The Private Browsing menu item should read \"New Private Window\"");
+      is(PrivateBrowsingUtils.isWindowPrivate(aWindow), aIsPrivateMode,
+        "PrivateBrowsingUtils should report the correct per-window private browsing status (privateBrowsing should be " +
+        aIsPrivateMode + ")");
+
+      aCallback();
+    }, true);
+
+    aWindow.gBrowser.selectedBrowser.loadURI(testURI);
+  };
+
+  function openPrivateBrowsingModeByUI(aWindow, aCallback) {
+    Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
       aSubject.addEventListener("load", function() {
         aSubject.removeEventListener("load", arguments.callee);
-        executeSoon(function() {
-          let ui = aSubject.gPrivateBrowsingUI;
-          is(ui.privateBrowsingEnabled, expected, "The privateBrowsingEnabled property on the new window is set correctly");
-          is(PrivateBrowsingUtils.isWindowPrivate(aSubject), expected, "The private bit on the new window is set correctly");
-
-          Services.obs.addObserver(function observer2(aSubject, aTopic, aData) {
-            aCallback();
-            Services.obs.removeObserver(observer2, "domwindowclosed");
-          }, "domwindowclosed", false);
-          aSubject.close();
-        });
-        Services.obs.removeObserver(observer1, "domwindowopened");
+          Services.obs.removeObserver(observer, "domwindowopened");
+          windowsToClose.push(aSubject);
+          aCallback(aSubject);
       }, false);
     }, "domwindowopened", false);
-    OpenBrowserWindow();
-  }
+
+    cmd = aWindow.document.getElementById("Tools:PrivateBrowsing");
+    var func = new Function("", cmd.getAttribute("oncommand"));
+    func.call(cmd);
+  };
 
-  // test the gPrivateBrowsingUI object
-  ok(gPrivateBrowsingUI, "The gPrivateBrowsingUI object exists");
-  is(pb.privateBrowsingEnabled, false, "The private browsing mode should not be started initially");
-  is(gPrivateBrowsingUI.privateBrowsingEnabled, false, "gPrivateBrowsingUI should expose the correct private browsing status");
-  is(PrivateBrowsingUtils.isWindowPrivate(window), false, "PrivateBrowsingUtils should expose the correct per-window private browsing status");
-  ok(pbMenuItem, "The Private Browsing menu item exists");
-  is(pbMenuItem.getAttribute("label"), pbMenuItem.getAttribute("startlabel"), "The Private Browsing menu item should read \"Start Private Browsing\"");
-  testNewWindow(function() {
-    gPrivateBrowsingUI.toggleMode();
-    is(pb.privateBrowsingEnabled, true, "The private browsing mode should be started");
-    is(gPrivateBrowsingUI.privateBrowsingEnabled, true, "gPrivateBrowsingUI should expose the correct private browsing status");
-    is(PrivateBrowsingUtils.isWindowPrivate(window), true, "PrivateBrowsingUtils should expose the correct per-window private browsing status");
-    // check to see if the Private Browsing mode was activated successfully
-    is(observerData, "enter", "Private Browsing mode was activated using the gPrivateBrowsingUI object");
-    is(pbMenuItem.getAttribute("label"), pbMenuItem.getAttribute("stoplabel"), "The Private Browsing menu item should read \"Stop Private Browsing\"");
-    testNewWindow(function() {
-      gPrivateBrowsingUI.toggleMode()
-      is(pb.privateBrowsingEnabled, false, "The private browsing mode should not be started");
-      is(gPrivateBrowsingUI.privateBrowsingEnabled, false, "gPrivateBrowsingUI should expose the correct private browsing status");
-      is(PrivateBrowsingUtils.isWindowPrivate(window), false, "PrivateBrowsingUtils should expose the correct per-window private browsing status");
-      // check to see if the Private Browsing mode was deactivated successfully
-      is(observerData, "exit", "Private Browsing mode was deactivated using the gPrivateBrowsingUI object");
-      is(pbMenuItem.getAttribute("label"), pbMenuItem.getAttribute("startlabel"), "The Private Browsing menu item should read \"Start Private Browsing\"");
+  function testOnWindow(aOptions, aCallback) {
+    whenNewWindowLoaded(aOptions, function(aWin) {
+      windowsToClose.push(aWin);
+      // execute should only be called when need, like when you are opening
+      // web pages on the test. If calling executeSoon() is not necesary, then
+      // call whenNewWindowLoaded() instead of testOnWindow() on your test.
+      executeSoon(function() aCallback(aWin));
+    });
+  };
 
-      testNewWindow(function() {
-        // These are tests for the private bit setter.  Note that the setter should
-        // not be used anywhere else for now!
-        setPrivateWindow(window, true);
-        is(PrivateBrowsingUtils.isWindowPrivate(window), true, "PrivateBrowsingUtils should accept the correct per-window private browsing status");
-        setPrivateWindow(window, false);
-        is(PrivateBrowsingUtils.isWindowPrivate(window), false, "PrivateBrowsingUtils should accept the correct per-window private browsing status");
+   // this function is called after calling finish() on the test.
+  registerCleanupFunction(function() {
+    windowsToClose.forEach(function(aWin) {
+      aWin.close();
+    });
+  });
 
-        // now, test using the <command> object
-        let cmd = document.getElementById("Tools:PrivateBrowsing");
-        isnot(cmd, null, "XUL command object for the private browsing service exists");
-        var func = new Function("", cmd.getAttribute("oncommand"));
-        func.call(cmd);
-        // check to see if the Private Browsing mode was activated successfully
-        is(observerData, "enter", "Private Browsing mode was activated using the command object");
-        // check to see that the window title has been changed correctly
-        isnot(document.title, originalTitle, "Private browsing mode has correctly changed the title");
-        func.call(cmd);
-        // check to see if the Private Browsing mode was deactivated successfully
-        is(observerData, "exit", "Private Browsing mode was deactivated using the command object");
-        // check to see that the window title has been restored correctly
-        is(document.title, originalTitle, "Private browsing mode has correctly restored the title");
-
-        // cleanup
-        gBrowser.removeCurrentTab();
-        Services.obs.removeObserver(observer, "private-browsing");
-        gPrefService.clearUserPref("browser.privatebrowsing.keep_current_session");
-
-        finish();
-      }, false);
-    }, true);
-  }, false);
+  // test first when not on private mode
+  testOnWindow({}, function(aWin) {
+    doTest(false, aWin, function() {
+      // then test when on private mode, opening a new private window from the
+      // user interface.
+      openPrivateBrowsingModeByUI(aWin, function(aPrivateWin) {
+        doTest(true, aPrivateWin, finish);
+      });
+    });
+  });
 }
copy from browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_zoom.js
copy to browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_zoom.js
--- a/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_zoom.js
+++ b/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_zoom.js
@@ -1,62 +1,59 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This test makes sure that private browsing turns off doesn't cause zoom
 // settings to be reset on tab switch (bug 464962)
 
 function test() {
-  // initialization
-  gPrefService.setBoolPref("browser.privatebrowsing.keep_current_session", true);
-  let pb = Cc["@mozilla.org/privatebrowsing;1"].
-           getService(Ci.nsIPrivateBrowsingService);
-
-  // enter private browsing mode
-  pb.privateBrowsingEnabled = true;
-
-  let tabAbout = gBrowser.addTab();
-  gBrowser.selectedTab = tabAbout;
-
   waitForExplicitFinish();
 
-  let aboutBrowser = gBrowser.getBrowserForTab(tabAbout);
-  aboutBrowser.addEventListener("load", function onAboutBrowserLoad() {
-    aboutBrowser.removeEventListener("load", onAboutBrowserLoad, true);
-    let tabMozilla = gBrowser.addTab();
-    gBrowser.selectedTab = tabMozilla;
+  function testZoom(aWindow, aCallback) {
+    executeSoon(function() {
+      let tabAbout = aWindow.gBrowser.addTab();
+      aWindow.gBrowser.selectedTab = tabAbout;
+
+      let aboutBrowser = aWindow.gBrowser.getBrowserForTab(tabAbout);
+      aboutBrowser.addEventListener("load", function onAboutBrowserLoad() {
+        aboutBrowser.removeEventListener("load", onAboutBrowserLoad, true);
+        let tabMozilla = aWindow.gBrowser.addTab();
+        aWindow.gBrowser.selectedTab = tabMozilla;
 
-    let mozillaBrowser = gBrowser.getBrowserForTab(tabMozilla);
-    mozillaBrowser.addEventListener("load", function onMozillaBrowserLoad() {
-      mozillaBrowser.removeEventListener("load", onMozillaBrowserLoad, true);
-      let mozillaZoom = ZoomManager.zoom;
+        let mozillaBrowser = aWindow.gBrowser.getBrowserForTab(tabMozilla);
+        mozillaBrowser.addEventListener("load", function onMozillaBrowserLoad() {
+          mozillaBrowser.removeEventListener("load", onMozillaBrowserLoad, true);
+          let mozillaZoom = aWindow.ZoomManager.zoom;
 
-      // change the zoom on the mozilla page
-      FullZoom.enlarge();
-      // make sure the zoom level has been changed
-      isnot(ZoomManager.zoom, mozillaZoom, "Zoom level can be changed");
-      mozillaZoom = ZoomManager.zoom;
+          // change the zoom on the mozilla page
+          aWindow.FullZoom.enlarge();
+          // make sure the zoom level has been changed
+          isnot(aWindow.ZoomManager.zoom, mozillaZoom, "Zoom level can be changed");
+          mozillaZoom = aWindow.ZoomManager.zoom;
 
-      // switch to about: tab
-      gBrowser.selectedTab = tabAbout;
+          // switch to about: tab
+          aWindow.gBrowser.selectedTab = tabAbout;
 
-      // switch back to mozilla tab
-      gBrowser.selectedTab = tabMozilla;
+          // switch back to mozilla tab
+          aWindow.gBrowser.selectedTab = tabMozilla;
 
-      // make sure the zoom level has not changed
-      is(ZoomManager.zoom, mozillaZoom,
-        "Entering private browsing should not reset the zoom on a tab");
-
-      // leave private browsing mode
-      pb.privateBrowsingEnabled = false;
+          // make sure the zoom level has not changed
+          is(aWindow.ZoomManager.zoom, mozillaZoom,
+            "Entering private browsing should not reset the zoom on a tab");
 
-      // cleanup
-      gPrefService.clearUserPref("browser.privatebrowsing.keep_current_session");
-      FullZoom.reset();
-      gBrowser.removeTab(tabMozilla);
-      gBrowser.removeTab(tabAbout);
-      finish();
-    }, true);
-    mozillaBrowser.contentWindow.location = "about:mozilla";
-  }, true);
-  aboutBrowser.contentWindow.location = "about:";
+          // cleanup
+          aWindow.FullZoom.reset();
+          aWindow.gBrowser.removeTab(tabMozilla);
+          aWindow.gBrowser.removeTab(tabAbout);
+          aWindow.close();
+          aCallback();
+        }, true);
+        mozillaBrowser.contentWindow.location = "about:mozilla";
+      }, true);
+      aboutBrowser.contentWindow.location = "about:";
+    });
+  }
+
+  whenNewWindowLoaded({private: true}, function(privateWindow) {
+    testZoom(privateWindow, finish);
+  });
 }
--- a/browser/devtools/commandline/test/Makefile.in
+++ b/browser/devtools/commandline/test/Makefile.in
@@ -19,17 +19,16 @@ MOCHITEST_BROWSER_FILES = \
   browser_cmd_calllog_chrome.js \
   browser_cmd_commands.js \
   browser_cmd_cookie.js \
   browser_cmd_integrate.js \
   browser_cmd_jsb.js \
   browser_cmd_pagemod_export.js \
   browser_cmd_pref.js \
   browser_cmd_restart.js \
-  browser_cmd_screenshot.js \
   browser_cmd_settings.js \
   browser_gcli_canon.js \
   browser_gcli_cli.js \
   browser_gcli_completion.js \
   browser_gcli_exec.js \
   browser_gcli_focus.js \
   browser_gcli_history.js \
   browser_gcli_incomplete.js \
@@ -44,19 +43,30 @@ MOCHITEST_BROWSER_FILES = \
   browser_gcli_spell.js \
   browser_gcli_split.js \
   browser_gcli_tokenize.js \
   browser_gcli_tooltip.js \
   browser_gcli_types.js \
   browser_gcli_util.js \
   head.js \
   helpers.js \
+  helpers_perwindowpb.js \
   mockCommands.js \
   $(NULL)
 
+ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+MOCHITEST_BROWSER_FILES += \
+ browser_cmd_screenshot_perwindowpb.js \
+ $(NULL)
+else
+MOCHITEST_BROWSER_FILES += \
+ browser_cmd_screenshot.js \
+ $(NULL)
+endif
+
 MOCHITEST_BROWSER_FILES += \
   browser_dbg_cmd_break.html \
   browser_dbg_cmd.html \
   browser_cmd_screenshot.html \
   browser_cmd_pagemod_export.html \
   browser_cmd_jsb_script.jsi \
   $(NULL)
 
copy from browser/devtools/commandline/test/browser_cmd_screenshot.js
copy to browser/devtools/commandline/test/browser_cmd_screenshot_perwindowpb.js
--- a/browser/devtools/commandline/test/browser_cmd_screenshot.js
+++ b/browser/devtools/commandline/test/browser_cmd_screenshot_perwindowpb.js
@@ -1,79 +1,98 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that screenshot command works properly
 const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
                  "test/browser_cmd_screenshot.html";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-let tempScope = {};
-Cu.import("resource://gre/modules/FileUtils.jsm", tempScope);
-let FileUtils = tempScope.FileUtils;
+let FileUtils = Cu.import("resource://gre/modules/FileUtils.jsm", {}).FileUtils;
+
+function test() {
+  waitForExplicitFinish();
+
+  function testOnWindow(aPrivate, aCallback) {
+    let win = OpenBrowserWindow({private: aPrivate});
+    win.addEventListener("load", function onLoad() {
+      win.removeEventListener("load", onLoad, false);
+      executeSoon(function() aCallback(win));
+    }, false);
+  };
 
-let pb = Cc["@mozilla.org/privatebrowsing;1"]
-           .getService(Ci.nsIPrivateBrowsingService);
-function test() {
-  DeveloperToolbarTest.test(TEST_URI, [ testInput, testCapture ]);
+  testOnWindow(false, function(win) {
+    DeveloperToolbarTestPW.test(win, TEST_URI, [ testInput, testCapture ], null, function() {
+      win.close();
+      testOnWindow(true, function(win) {
+        executeSoon(function() {
+          DeveloperToolbarTestPW.test(win, TEST_URI, [ testInput, testCapture ], null, function() {
+            win.close();
+            finish();
+          });
+        })
+      });
+    });
+  });
+
 }
 
-function testInput() {
-  helpers.setInput('screenshot');
-  helpers.check({
+function testInput(aWindow) {
+  helpers_perwindowpb.setInput('screenshot');
+  helpers_perwindowpb.check({
     input:  'screenshot',
     markup: 'VVVVVVVVVV',
     status: 'VALID',
     args: {
     }
   });
 
-  helpers.setInput('screenshot abc.png');
-  helpers.check({
+  helpers_perwindowpb.setInput('screenshot abc.png');
+  helpers_perwindowpb.check({
     input:  'screenshot abc.png',
     markup: 'VVVVVVVVVVVVVVVVVV',
     status: 'VALID',
     args: {
       filename: { value: "abc.png"},
     }
   });
 
-  helpers.setInput('screenshot --fullpage');
-  helpers.check({
+  helpers_perwindowpb.setInput('screenshot --fullpage');
+  helpers_perwindowpb.check({
     input:  'screenshot --fullpage',
     markup: 'VVVVVVVVVVVVVVVVVVVVV',
     status: 'VALID',
     args: {
       fullpage: { value: true},
     }
   });
 
-  helpers.setInput('screenshot abc --delay 5');
-  helpers.check({
+  helpers_perwindowpb.setInput('screenshot abc --delay 5');
+  helpers_perwindowpb.check({
     input:  'screenshot abc --delay 5',
     markup: 'VVVVVVVVVVVVVVVVVVVVVVVV',
     status: 'VALID',
     args: {
       filename: { value: "abc"},
       delay: { value: "5"},
     }
   });
 
-  helpers.setInput('screenshot --selector img#testImage');
-  helpers.check({
+  helpers_perwindowpb.setInput('screenshot --selector img#testImage');
+  helpers_perwindowpb.check({
     input:  'screenshot --selector img#testImage',
     markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
     status: 'VALID',
     args: {
-      selector: { value: content.document.getElementById("testImage")},
+      selector: { value: aWindow.content.document.getElementById("testImage")},
     }
   });
 }
 
-function testCapture() {
+function testCapture(aWindow) {
   function checkTemporaryFile() {
     // Create a temporary file.
     let gFile = FileUtils.getFile("TmpD", ["TestScreenshotFile.png"]);
     if (gFile.exists()) {
       gFile.remove(false);
       return true;
     }
     else {
@@ -96,84 +115,57 @@ function testCapture() {
       if (str.value && strLength.value > 0) {
         return true;
       }
     }
     catch (ex) {}
     return false;
   }
 
-  let PBEntered = DeveloperToolbarTest.checkCalled(function() {
-    Services.obs.removeObserver(PBEntered,
-                                "private-browsing-transition-complete",
-                                false);
-
-    Services.obs.addObserver(PBLeft, "last-pb-context-exited", false);
-
-    DeveloperToolbarTest.exec({
-      typed: "screenshot --clipboard",
-      args: {
-        delay: 0,
-        filename: " ",
-        fullpage: false,
-        clipboard: true,
-        node: null,
-        chrome: false,
-      },
-      outputMatch: new RegExp("^Copied to clipboard.$"),
-    });
-
-    ok(checkClipboard(), "Screenshot present in clipboard in private browsing");
-
-    pb.privateBrowsingEnabled = false;
-  });
-
-  let PBLeft = DeveloperToolbarTest.checkCalled(function() {
-    Services.obs.removeObserver(PBLeft, "last-pb-context-exited", false);
-    executeSoon(function() {
-      ok(!checkClipboard(), "Screenshot taken in private browsing mode is not" +
-                            " present outside of it in the clipboard");
-      Services.prefs.clearUserPref("browser.privatebrowsing.keep_current_session");
-      pb = null;
-    });
-  });
-
   let path = FileUtils.getFile("TmpD", ["TestScreenshotFile.png"]).path;
 
-  DeveloperToolbarTest.exec({
+  DeveloperToolbarTestPW.exec(aWindow, {
     typed: "screenshot " + path,
     args: {
       delay: 0,
       filename: "" + path,
       fullpage: false,
       clipboard: false,
       node: null,
       chrome: false,
     },
     outputMatch: new RegExp("^Saved to "),
   });
 
-  Services.obs.addObserver(PBEntered, "private-browsing-transition-complete",
-                           false);
-
   executeSoon(function() {
     ok(checkTemporaryFile(), "Screenshot got created");
+  });
 
-    DeveloperToolbarTest.exec({
-      typed: "screenshot --fullpage --clipboard",
-      args: {
-        delay: 0,
-        filename: " ",
-        fullpage: true,
-        clipboard: true,
-        node: null,
-        chrome: false,
-      },
-      outputMatch: new RegExp("^Copied to clipboard.$"),
-    });
+  DeveloperToolbarTestPW.exec(aWindow, {
+    typed: "screenshot --fullpage --clipboard",
+    args: {
+      delay: 0,
+      filename: " ",
+      fullpage: true,
+      clipboard: true,
+      node: null,
+      chrome: false,
+    },
+    outputMatch: new RegExp("^Copied to clipboard.$"),
+  });
+
+  ok(checkClipboard(), "Screenshot got created and copied");
 
-    ok(checkClipboard(), "Screenshot got created and copied");
-
-    Services.prefs.setBoolPref("browser.privatebrowsing.keep_current_session", true);
+  DeveloperToolbarTestPW.exec(aWindow, {
+    typed: "screenshot --clipboard",
+    args: {
+      delay: 0,
+      filename: " ",
+      fullpage: false,
+      clipboard: true,
+      node: null,
+      chrome: false,
+    },
+    outputMatch: new RegExp("^Copied to clipboard.$"),
+  });
 
-    pb.privateBrowsingEnabled = true;
-  });
+  ok(checkClipboard(), "Screenshot present in clipboard");
 }
--- a/browser/devtools/commandline/test/head.js
+++ b/browser/devtools/commandline/test/head.js
@@ -11,16 +11,17 @@ let console = (function() {
   return tempScope.console;
 })();
 
 // Import the GCLI test helper
 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 
 Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
 Services.scriptloader.loadSubScript(testDir + "/mockCommands.js", this);
+Services.scriptloader.loadSubScript(testDir + "/helpers_perwindowpb.js", this);
 
 /**
  * Open a new tab at a URL and call a callback on load
  */
 function addTab(aURL, aCallback)
 {
   waitForExplicitFinish();
 
copy from browser/devtools/commandline/test/helpers.js
copy to browser/devtools/commandline/test/helpers_perwindowpb.js
--- a/browser/devtools/commandline/test/helpers.js
+++ b/browser/devtools/commandline/test/helpers_perwindowpb.js
@@ -29,70 +29,70 @@
  *  FOR A LONG TIME.
  *
  */
 
 
 /*
  * Use as a JSM
  * ------------
- * helpers._createDebugCheck() and maybe other functions in this file can be
- * useful at runtime, so it is possible to use helpers.js as a JSM.
- * Copy commandline/test/helpers.js to shared/helpers.jsm, and then add to
+ * helpers_perwindowpb._createDebugCheck() and maybe other functions in this file can be
+ * useful at runtime, so it is possible to use helpers_perwindowpb.js as a JSM.
+ * Copy commandline/test/helpers_perwindowpb.js to shared/helpers_perwindowpb.jsm, and then add to
  * DeveloperToolbar.jsm the following:
  *
- * XPCOMUtils.defineLazyModuleGetter(this, "helpers",
- *                                 "resource:///modules/devtools/helpers.jsm");
+ * XPCOMUtils.defineLazyModuleGetter(this, "helpers_perwindowpb",
+ *                                 "resource:///modules/devtools/helpers_perwindowpb.jsm");
  *
  * At the bottom of DeveloperToolbar.prototype._onload add this:
  *
  * var options = { display: this.display };
  * this._input.onkeypress = function(ev) {
- *   helpers.setup(options);
- *   dump(helpers._createDebugCheck() + '\n\n');
+ *   helpers_perwindowpb.setup(options);
+ *   dump(helpers_perwindowpb._createDebugCheck() + '\n\n');
  * };
  *
  * Now GCLI will emit output on every keypress that both explains the state
  * of GCLI and can be run as a test case.
  */
 
-this.EXPORTED_SYMBOLS = [ 'helpers' ];
+this.EXPORTED_SYMBOLS = [ 'helpers_perwindowpb' ];
 
 var test = { };
 
 /**
  * Various functions for testing DeveloperToolbar.
  * Parts of this code exist in:
  * - browser/devtools/commandline/test/head.js
  * - browser/devtools/shared/test/head.js
  */
-let DeveloperToolbarTest = { };
+let DeveloperToolbarTestPW = { };
 
 /**
  * Paranoid DeveloperToolbar.show();
  */
-DeveloperToolbarTest.show = function DTT_show(aCallback) {
-  if (DeveloperToolbar.visible) {
+DeveloperToolbarTestPW.show = function DTTPW_show(aWindow, aCallback) {
+  if (aWindow.DeveloperToolbar.visible) {
     ok(false, "DeveloperToolbar.visible at start of openDeveloperToolbar");
   }
   else {
-    DeveloperToolbar.show(true, aCallback);
+    aWindow.DeveloperToolbar.show(true, aCallback);
   }
 };
 
 /**
  * Paranoid DeveloperToolbar.hide();
  */
-DeveloperToolbarTest.hide = function DTT_hide() {
-  if (!DeveloperToolbar.visible) {
+DeveloperToolbarTestPW.hide = function DTTPW_hide(aWindow) {
+  if (!aWindow.DeveloperToolbar.visible) {
     ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
   }
   else {
-    DeveloperToolbar.display.inputter.setInput("");
-    DeveloperToolbar.hide();
+    aWindow.DeveloperToolbar.display.inputter.setInput("");
+    aWindow.DeveloperToolbar.hide();
   }
 };
 
 /**
  * check() is the new status. Similar API except that it doesn't attempt to
  * alter the display/requisition at all, and it makes extra checks.
  * Test inputs
  *   typed: The text to type at the input
@@ -109,28 +109,28 @@ DeveloperToolbarTest.hide = function DTT
  *     type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
  *           Care should be taken with this since it's something of an
  *           implementation detail
  *     arg: The toString value of the argument
  *     status: i.e. assignment.getStatus
  *     message: i.e. assignment.getMessage
  *     name: For commands - checks assignment.value.name
  */
-DeveloperToolbarTest.checkInputStatus = function DTT_checkInputStatus(checks) {
+DeveloperToolbarTestPW.checkInputStatus = function DTTPW_checkInputStatus(aWindow, checks) {
   if (!checks.emptyParameters) {
     checks.emptyParameters = [];
   }
   if (!checks.directTabText) {
     checks.directTabText = '';
   }
   if (!checks.arrowTabText) {
     checks.arrowTabText = '';
   }
 
-  var display = DeveloperToolbar.display;
+  var display = aWindow.DeveloperToolbar.display;
 
   if (checks.typed) {
     info('Starting tests for ' + checks.typed);
     display.inputter.setInput(checks.typed);
   }
   else {
     ok(false, "Missing typed for " + JSON.stringify(checks));
     return;
@@ -261,36 +261,36 @@ DeveloperToolbarTest.checkInputStatus = 
       }
     });
   }
 };
 
 /**
  * Execute a command:
  *
- * DeveloperToolbarTest.exec({
+ * DeveloperToolbarTestPW.exec({
  *   // Test inputs
  *   typed: "echo hi",        // Optional, uses existing if undefined
  *
  *   // Thing to check
  *   args: { message: "hi" }, // Check that the args were understood properly
  *   outputMatch: /^hi$/,     // RegExp to test against textContent of output
  *                            // (can also be array of RegExps)
  *   blankOutput: true,       // Special checks when there is no output
  * });
  */
-DeveloperToolbarTest.exec = function DTT_exec(tests) {
+DeveloperToolbarTestPW.exec = function DTTPW_exec(aWindow, tests) {
   tests = tests || {};
 
   if (tests.typed) {
-    DeveloperToolbar.display.inputter.setInput(tests.typed);
+    aWindow.DeveloperToolbar.display.inputter.setInput(tests.typed);
   }
 
-  let typed = DeveloperToolbar.display.inputter.getInputState().typed;
-  let output = DeveloperToolbar.display.requisition.exec();
+  let typed = aWindow.DeveloperToolbar.display.inputter.getInputState().typed;
+  let output = aWindow.DeveloperToolbar.display.requisition.exec();
 
   is(typed, output.typed, 'output.command for: ' + typed);
 
   if (tests.completed !== false) {
     ok(output.completed, 'output.completed false for: ' + typed);
   }
   else {
     // It is actually an error if we say something is async and it turns
@@ -325,17 +325,17 @@ DeveloperToolbarTest.exec = function DTT
         }
         else {
           is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
         }
       }
     });
   }
 
-  let displayed = DeveloperToolbar.outputPanel._div.textContent;
+  let displayed = aWindow.DeveloperToolbar.outputPanel._div.textContent;
 
   if (tests.outputMatch) {
     var doTest = function(match, against) {
       if (!match.test(against)) {
         ok(false, "html output for " + typed + " against " + match.source +
                 " (textContent sent to info)");
         info("Actual textContent");
         info(against);
@@ -370,241 +370,245 @@ DeveloperToolbarTest.exec = function DTT
  * - Run the tests
  *
  * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
  * @param target Either a function or array of functions containing the tests
  * to run. If an array of test function is passed then we will clear up after
  * the tests have completed. If a single test function is passed then this
  * function should arrange for 'finish()' to be called on completion.
  */
-DeveloperToolbarTest.test = function DTT_test(uri, target, isGcli) {
-  let menuItem = document.getElementById("menu_devToolbar");
-  let command = document.getElementById("Tools:DevToolbar");
-  let appMenuItem = document.getElementById("appmenu_devToolbar");
+DeveloperToolbarTestPW.test = function DTTPW_test(aWindow, uri, target, isGcli, aCallback) {
+  let menuItem = aWindow.document.getElementById("menu_devToolbar");
+  let command = aWindow.document.getElementById("Tools:DevToolbar");
+  let appMenuItem = aWindow.document.getElementById("appmenu_devToolbar");
 
-  registerCleanupFunction(function() {
-    DeveloperToolbarTest.hide();
+  function cleanup() {
+    DeveloperToolbarTestPW.hide(aWindow);
 
     // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
     if (menuItem) {
       menuItem.hidden = true;
     }
     if (command) {
       command.setAttribute("disabled", "true");
     }
     if (appMenuItem) {
       appMenuItem.hidden = true;
     }
 
     // leakHunt({ DeveloperToolbar: DeveloperToolbar });
-  });
+  };
 
   // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
   if (menuItem) {
     menuItem.hidden = false;
   }
   if (command) {
     command.removeAttribute("disabled");
   }
   if (appMenuItem) {
     appMenuItem.hidden = false;
   }
 
-  waitForExplicitFinish();
+  aWindow.gBrowser.selectedTab = aWindow.gBrowser.addTab();
+  aWindow.content.location = uri;
 
-  gBrowser.selectedTab = gBrowser.addTab();
-  content.location = uri;
-
-  let tab = gBrowser.selectedTab;
-  let browser = gBrowser.getBrowserForTab(tab);
+  let tab = aWindow.gBrowser.selectedTab;
+  let browser = aWindow.gBrowser.getBrowserForTab(tab);
 
   var onTabLoad = function() {
     browser.removeEventListener("load", onTabLoad, true);
 
-    DeveloperToolbarTest.show(function() {
+    DeveloperToolbarTestPW.show(aWindow, function() {
       var options = {
-        window: content,
-        display: DeveloperToolbar.display,
+        window: aWindow.content,
+        display: aWindow.DeveloperToolbar.display,
         isFirefox: true
       };
 
-      if (helpers && !isGcli) {
-        helpers.setup(options);
+      if (helpers_perwindowpb && !isGcli) {
+        helpers_perwindowpb.setup(options);
       }
 
       if (Array.isArray(target)) {
         try {
           target.forEach(function(func) {
-            var args = isGcli ? [ options ] : [ browser, tab ];
+            var args = isGcli ? [ options ] : [ aWindow ];
             func.apply(null, args);
-          })
+          });
+          cleanup();
+          aCallback();
         }
-        finally {
-          DeveloperToolbarTest._checkFinish();
+        catch (ex) {
+          ok(false, "" + ex);
+          DeveloperToolbarTestPW._finish(aWindow);
+          throw ex;
         }
       }
       else {
         try {
-          target(browser, tab);
+          target(aWindow);
+          cleanup();
+          aCallback();
         }
         catch (ex) {
           ok(false, "" + ex);
-          DeveloperToolbarTest._finish();
+          DeveloperToolbarTestPW._finish(aWindow);
           throw ex;
         }
       }
     });
   }
 
   browser.addEventListener("load", onTabLoad, true);
 };
 
-DeveloperToolbarTest._outstanding = [];
+DeveloperToolbarTestPW._outstanding = [];
 
-DeveloperToolbarTest._checkFinish = function() {
-  info('_checkFinish. ' + DeveloperToolbarTest._outstanding.length + ' outstanding');
-  if (DeveloperToolbarTest._outstanding.length == 0) {
-    DeveloperToolbarTest._finish();
+DeveloperToolbarTestPW._checkFinish = function(aWindow) {
+  info('_checkFinish. ' + DeveloperToolbarTestPW._outstanding.length + ' outstanding');
+  if (DeveloperToolbarTestPW._outstanding.length == 0) {
+    DeveloperToolbarTestPW._finish(aWindow);
   }
 }
 
-DeveloperToolbarTest._finish = function() {
+DeveloperToolbarTestPW._finish = function(aWindow) {
   info('Finish');
-  DeveloperToolbarTest.closeAllTabs();
+  DeveloperToolbarTestPW.closeAllTabs(aWindow);
   finish();
 }
 
-DeveloperToolbarTest.checkCalled = function(aFunc, aScope) {
+DeveloperToolbarTestPW.checkCalled = function(aWindow, aFunc, aScope) {
   var todo = function() {
     var reply = aFunc.apply(aScope, arguments);
-    DeveloperToolbarTest._outstanding = DeveloperToolbarTest._outstanding.filter(function(aJob) {
+    DeveloperToolbarTestPW._outstanding = DeveloperToolbarTestPW._outstanding.filter(function(aJob) {
       return aJob != todo;
     });
-    DeveloperToolbarTest._checkFinish();
+    DeveloperToolbarTestPW._checkFinish(aWindow);
     return reply;
   }
-  DeveloperToolbarTest._outstanding.push(todo);
+  DeveloperToolbarTestPW._outstanding.push(todo);
   return todo;
 };
 
-DeveloperToolbarTest.checkNotCalled = function(aMsg, aFunc, aScope) {
+DeveloperToolbarTestPW.checkNotCalled = function(aMsg, aFunc, aScope) {
   return function() {
     ok(false, aMsg);
     return aFunc.apply(aScope, arguments);
   }
 };
 
 /**
  *
  */
-DeveloperToolbarTest.closeAllTabs = function() {
-  while (gBrowser.tabs.length > 1) {
-    gBrowser.removeCurrentTab();
+DeveloperToolbarTestPW.closeAllTabs = function(aWindow) {
+  while (aWindow.gBrowser.tabs.length > 1) {
+    aWindow.gBrowser.removeCurrentTab();
   }
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
-this.helpers = {};
+this.helpers_perwindowpb = {};
 
 var assert = { ok: ok, is: is, log: info };
 
-helpers._display = undefined;
-helpers._options = undefined;
+helpers_perwindowpb._display = undefined;
+helpers_perwindowpb._options = undefined;
 
-helpers.setup = function(options) {
-  helpers._options = options;
-  helpers._display = options.display;
+helpers_perwindowpb.setup = function(options) {
+  helpers_perwindowpb._options = options;
+  helpers_perwindowpb._display = options.display;
 };
 
-helpers.shutdown = function(options) {
-  helpers._options = undefined;
-  helpers._display = undefined;
+helpers_perwindowpb.shutdown = function(options) {
+  helpers_perwindowpb._options = undefined;
+  helpers_perwindowpb._display = undefined;
 };
 
 /**
  * Various functions to return the actual state of the command line
  */
-helpers._actual = {
+helpers_perwindowpb._actual = {
   input: function() {
-    return helpers._display.inputter.element.value;
+    return helpers_perwindowpb._display.inputter.element.value;
   },
 
   hints: function() {
-    var templateData = helpers._display.completer._getCompleterTemplateData();
+    var templateData = helpers_perwindowpb._display.completer._getCompleterTemplateData();
     var actualHints = templateData.directTabText +
                       templateData.emptyParameters.join('') +
                       templateData.arrowTabText;
     return actualHints.replace(/\u00a0/g, ' ')
                       .replace(/\u21E5/, '->')
                       .replace(/ $/, '');
   },
 
   markup: function() {
-    var cursor = helpers._display.inputter.element.selectionStart;
-    var statusMarkup = helpers._display.requisition.getInputStatusMarkup(cursor);
+    var cursor = helpers_perwindowpb._display.inputter.element.selectionStart;
+    var statusMarkup = helpers_perwindowpb._display.requisition.getInputStatusMarkup(cursor);
     return statusMarkup.map(function(s) {
       return Array(s.string.length + 1).join(s.status.toString()[0]);
     }).join('');
   },
 
   cursor: function() {
-    return helpers._display.inputter.element.selectionStart;
+    return helpers_perwindowpb._display.inputter.element.selectionStart;
   },
 
   current: function() {
-    return helpers._display.requisition.getAssignmentAt(helpers._actual.cursor()).param.name;
+    return helpers_perwindowpb._display.requisition.getAssignmentAt(helpers_perwindowpb._actual.cursor()).param.name;
   },
 
   status: function() {
-    return helpers._display.requisition.getStatus().toString();
+    return helpers_perwindowpb._display.requisition.getStatus().toString();
   },
 
   outputState: function() {
-    var outputData = helpers._display.focusManager._shouldShowOutput();
+    var outputData = helpers_perwindowpb._display.focusManager._shouldShowOutput();
     return outputData.visible + ':' + outputData.reason;
   },
 
   tooltipState: function() {
-    var tooltipData = helpers._display.focusManager._shouldShowTooltip();
+    var tooltipData = helpers_perwindowpb._display.focusManager._shouldShowTooltip();
     return tooltipData.visible + ':' + tooltipData.reason;
   }
 };
 
-helpers._directToString = [ 'boolean', 'undefined', 'number' ];
+helpers_perwindowpb._directToString = [ 'boolean', 'undefined', 'number' ];
 
-helpers._createDebugCheck = function() {
-  var requisition = helpers._display.requisition;
+helpers_perwindowpb._createDebugCheck = function() {
+  var requisition = helpers_perwindowpb._display.requisition;
   var command = requisition.commandAssignment.value;
-  var input = helpers._actual.input();
+  var input = helpers_perwindowpb._actual.input();
   var padding = Array(input.length + 1).join(' ');
 
   var output = '';
-  output += 'helpers.setInput(\'' + input + '\');\n';
-  output += 'helpers.check({\n';
+  output += 'helpers_perwindowpb.setInput(\'' + input + '\');\n';
+  output += 'helpers_perwindowpb.check({\n';
   output += '  input:  \'' + input + '\',\n';
-  output += '  hints:  ' + padding + '\'' + helpers._actual.hints() + '\',\n';
-  output += '  markup: \'' + helpers._actual.markup() + '\',\n';
-  output += '  cursor: ' + helpers._actual.cursor() + ',\n';
-  output += '  current: \'' + helpers._actual.current() + '\',\n';
-  output += '  status: \'' + helpers._actual.status() + '\',\n';
-  output += '  outputState: \'' + helpers._actual.outputState() + '\',\n';
+  output += '  hints:  ' + padding + '\'' + helpers_perwindowpb._actual.hints() + '\',\n';
+  output += '  markup: \'' + helpers_perwindowpb._actual.markup() + '\',\n';
+  output += '  cursor: ' + helpers_perwindowpb._actual.cursor() + ',\n';
+  output += '  current: \'' + helpers_perwindowpb._actual.current() + '\',\n';
+  output += '  status: \'' + helpers_perwindowpb._actual.status() + '\',\n';
+  output += '  outputState: \'' + helpers_perwindowpb._actual.outputState() + '\',\n';
 
   if (command) {
-    output += '  tooltipState: \'' + helpers._actual.tooltipState() + '\',\n';
+    output += '  tooltipState: \'' + helpers_perwindowpb._actual.tooltipState() + '\',\n';
     output += '  args: {\n';
     output += '    command: { name: \'' + command.name + '\' },\n';
 
     requisition.getAssignments().forEach(function(assignment) {
       output += '    ' + assignment.param.name + ': { ';
 
       if (typeof assignment.value === 'string') {
         output += 'value: \'' + assignment.value + '\', ';
       }
-      else if (helpers._directToString.indexOf(typeof assignment.value) !== -1) {
+      else if (helpers_perwindowpb._directToString.indexOf(typeof assignment.value) !== -1) {
         output += 'value: ' + assignment.value + ', ';
       }
       else if (assignment.value === null) {
         output += 'value: ' + assignment.value + ', ';
       }
       else {
         output += '/*value:' + assignment.value + ',*/ ';
       }
@@ -613,84 +617,84 @@ helpers._createDebugCheck = function() {
       output += 'status: \'' + assignment.getStatus().toString() + '\', ';
       output += 'message: \'' + assignment.getMessage() + '\'';
       output += ' },\n';
     });
 
     output += '  }\n';
   }
   else {
-    output += '  tooltipState: \'' + helpers._actual.tooltipState() + '\'\n';
+    output += '  tooltipState: \'' + helpers_perwindowpb._actual.tooltipState() + '\'\n';
   }
   output += '});';
 
   return output;
 };
 
 /**
  * We're splitting status into setup() which alters the state of the system
  * and check() which ensures that things are in the right place afterwards.
  */
-helpers.setInput = function(typed, cursor) {
-  helpers._display.inputter.setInput(typed);
+helpers_perwindowpb.setInput = function(typed, cursor) {
+  helpers_perwindowpb._display.inputter.setInput(typed);
 
   if (cursor) {
-    helpers._display.inputter.setCursor({ start: cursor, end: cursor });
+    helpers_perwindowpb._display.inputter.setCursor({ start: cursor, end: cursor });
   }
   else {
     // This is a hack because jsdom appears to not handle cursor updates
     // in the same way as most browsers.
-    if (helpers._options.isJsdom) {
-      helpers._display.inputter.setCursor({
+    if (helpers_perwindowpb._options.isJsdom) {
+      helpers_perwindowpb._display.inputter.setCursor({
         start: typed.length,
         end: typed.length
       });
     }
   }
 
-  helpers._display.focusManager.onInputChange();
+  helpers_perwindowpb._display.focusManager.onInputChange();
 
   // Firefox testing is noisy and distant, so logging helps
-  if (helpers._options.isFirefox) {
+  if (helpers_perwindowpb._options.isFirefox) {
     assert.log('setInput("' + typed + '"' + (cursor == null ? '' : ', ' + cursor) + ')');
   }
 };
 
 /**
  * Simulate focusing the input field
  */
-helpers.focusInput = function() {
-  helpers._display.inputter.focus();
+helpers_perwindowpb.focusInput = function() {
+  helpers_perwindowpb._display.inputter.focus();
 };
 
 /**
  * Simulate pressing TAB in the input field
  */
-helpers.pressTab = function() {
-  helpers.pressKey(9 /*KeyEvent.DOM_VK_TAB*/);
+helpers_perwindowpb.pressTab = function() {
+  helpers_perwindowpb.pressKey(9 /*KeyEvent.DOM_VK_TAB*/);
 };
 
 /**
  * Simulate pressing RETURN in the input field
  */
-helpers.pressReturn = function() {
-  helpers.pressKey(13 /*KeyEvent.DOM_VK_RETURN*/);
+helpers_perwindowpb.pressReturn = function() {
+  helpers_perwindowpb.pressKey(13 /*KeyEvent.DOM_VK_RETURN*/);
 };
 
 /**
  * Simulate pressing a key by keyCode in the input field
  */
-helpers.pressKey = function(keyCode) {
+helpers_perwindowpb.pressKey = function(keyCode) {
   var fakeEvent = {
     keyCode: keyCode,
     preventDefault: function() { },
     timeStamp: new Date().getTime()
   };
-  helpers._display.inputter.onKeyDown(fakeEvent);
-  helpers._display.inputter.onKeyUp(fakeEvent);
+  helpers_perwindowpb._display.inputter.onKeyDown(fakeEvent);
+  helpers_perwindowpb._display.inputter.onKeyUp(fakeEvent);
 };
 
 /**
  * check() is the new status. Similar API except that it doesn't attempt to
  * alter the display/requisition at all, and it makes extra checks.
  * Available checks:
  *   input: The text displayed in the input field
  *   cursor: The position of the start of the cursor
@@ -705,51 +709,51 @@ helpers.pressKey = function(keyCode) {
  *     type: Argument/BlankArgument/MergedArgument/etc i.e. what's assigned
  *           Care should be taken with this since it's something of an
  *           implementation detail
  *     arg: The toString value of the argument
  *     status: i.e. assignment.getStatus
  *     message: i.e. assignment.getMessage
  *     name: For commands - checks assignment.value.name
  */
-helpers.check = function(checks) {
+helpers_perwindowpb.check = function(checks) {
   if ('input' in checks) {
-    assert.is(helpers._actual.input(), checks.input, 'input');
+    assert.is(helpers_perwindowpb._actual.input(), checks.input, 'input');
   }
 
   if ('cursor' in checks) {
-    assert.is(helpers._actual.cursor(), checks.cursor, 'cursor');
+    assert.is(helpers_perwindowpb._actual.cursor(), checks.cursor, 'cursor');
   }
 
   if ('current' in checks) {
-    assert.is(helpers._actual.current(), checks.current, 'current');
+    assert.is(helpers_perwindowpb._actual.current(), checks.current, 'current');
   }
 
   if ('status' in checks) {
-    assert.is(helpers._actual.status(), checks.status, 'status');
+    assert.is(helpers_perwindowpb._actual.status(), checks.status, 'status');
   }
 
   if ('markup' in checks) {
-    assert.is(helpers._actual.markup(), checks.markup, 'markup');
+    assert.is(helpers_perwindowpb._actual.markup(), checks.markup, 'markup');
   }
 
   if ('hints' in checks) {
-    assert.is(helpers._actual.hints(), checks.hints, 'hints');
+    assert.is(helpers_perwindowpb._actual.hints(), checks.hints, 'hints');
   }
 
   if ('tooltipState' in checks) {
-    assert.is(helpers._actual.tooltipState(), checks.tooltipState, 'tooltipState');
+    assert.is(helpers_perwindowpb._actual.tooltipState(), checks.tooltipState, 'tooltipState');
   }
 
   if ('outputState' in checks) {
-    assert.is(helpers._actual.outputState(), checks.outputState, 'outputState');
+    assert.is(helpers_perwindowpb._actual.outputState(), checks.outputState, 'outputState');
   }
 
   if (checks.args != null) {
-    var requisition = helpers._display.requisition;
+    var requisition = helpers_perwindowpb._display.requisition;
     Object.keys(checks.args).forEach(function(paramName) {
       var check = checks.args[paramName];
 
       var assignment;
       if (paramName === 'command') {
         assignment = requisition.commandAssignment;
       }
       else {
@@ -798,29 +802,29 @@ helpers.check = function(checks) {
       }
     });
   }
 };
 
 /**
  * Execute a command:
  *
- * helpers.exec({
+ * helpers_perwindowpb.exec({
  *   // Test inputs
  *   typed: "echo hi",        // Optional, uses existing if undefined
  *
  *   // Thing to check
  *   args: { message: "hi" }, // Check that the args were understood properly
  *   outputMatch: /^hi$/,     // Regex to test against textContent of output
  *   blankOutput: true,       // Special checks when there is no output
  * });
  */
-helpers.exec = function(tests) {
-  var requisition = helpers._display.requisition;
-  var inputter = helpers._display.inputter;
+helpers_perwindowpb.exec = function(tests) {
+  var requisition = helpers_perwindowpb._display.requisition;
+  var inputter = helpers_perwindowpb._display.inputter;
 
   tests = tests || {};
 
   if (tests.typed) {
     inputter.setInput(tests.typed);
   }
 
   var typed = inputter.getInputState().typed;
@@ -859,22 +863,22 @@ helpers.exec = function(tests) {
         }
       }
       else {
         assert.is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
       }
     });
   }
 
-  if (!helpers._options.window.document.createElement) {
+  if (!helpers_perwindowpb._options.window.document.createElement) {
     assert.log('skipping output tests (missing doc.createElement) for ' + typed);
     return;
   }
 
-  var div = helpers._options.window.document.createElement('div');
+  var div = helpers_perwindowpb._options.window.document.createElement('div');
   output.toDom(div);
   var displayed = div.textContent.trim();
 
   if (tests.outputMatch) {
     var doTest = function(match, against) {
       if (!match.test(against)) {
         assert.ok(false, "html output for " + typed + " against " + match.source);
         console.log("Actual textContent");
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -464,24 +464,27 @@ StackFrames.prototype = {
     }
 
     // Conditional breakpoints are { breakpoint, expression } tuples. The
     // boolean evaluation of the expression decides if the active thread
     // automatically resumes execution or not.
     if (this.currentBreakpointLocation) {
       let { url, line } = this.currentBreakpointLocation;
       let breakpointClient = DebuggerController.Breakpoints.getBreakpoint(url, line);
-      let conditionalExpression = breakpointClient.conditionalExpression;
-      if (conditionalExpression) {
-        // Evaluating the current breakpoint's conditional expression will
-        // cause the stack frames to be cleared and active thread to pause,
-        // sending a 'clientEvaluated' packed and adding the frames again.
-        this.evaluate(conditionalExpression, 0);
-        this._isConditionalBreakpointEvaluation = true;
-        return;
+      if (breakpointClient) {
+        // Make sure a breakpoint actually exists at the specified url and line.
+        let conditionalExpression = breakpointClient.conditionalExpression;
+        if (conditionalExpression) {
+          // Evaluating the current breakpoint's conditional expression will
+          // cause the stack frames to be cleared and active thread to pause,
+          // sending a 'clientEvaluated' packed and adding the frames again.
+          this.evaluate(conditionalExpression, 0);
+          this._isConditionalBreakpointEvaluation = true;
+          return;
+        }
       }
     }
     // Got our evaluation of the current breakpoint's conditional expression.
     if (this._isConditionalBreakpointEvaluation) {
       this._isConditionalBreakpointEvaluation = false;
       // If the breakpoint's conditional expression evaluation is falsy,
       // automatically resume execution.
       if (VariablesView.isFalsy({ value: this.currentEvaluation.return })) {
@@ -919,20 +922,23 @@ StackFrames.prototype = {
    * Updates a list of watch expressions to evaluate on each pause.
    */
   syncWatchExpressions: function SF_syncWatchExpressions() {
     let list = DebuggerView.WatchExpressions.getExpressions();
 
     if (list.length) {
       this.syncedWatchExpressions =
         this.currentWatchExpressions = "[" + list.map(function(str)
-          "(function() {" +
+          // Avoid yielding an empty pseudo-array when evaluating `arguments`,
+          // since they're overridden by the expression's closure scope.
+          "(function(arguments) {" +
+            // Make sure all the quotes are escaped in the expression's syntax.
             "try { return eval(\"" + str.replace(/"/g, "\\$&") + "\"); }" +
             "catch(e) { return e.name + ': ' + e.message; }" +
-          "})()"
+          "})(arguments)"
         ).join(",") + "]";
     } else {
       this.syncedWatchExpressions =
         this.currentWatchExpressions = null;
     }
     this._onFrames();
   },
 
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -152,16 +152,17 @@ function BreakpointsView() {
   dumpn("BreakpointsView was instantiated");
   MenuContainer.call(this);
   this._createItemView = this._createItemView.bind(this);
   this._onBreakpointRemoved = this._onBreakpointRemoved.bind(this);
   this._onEditorLoad = this._onEditorLoad.bind(this);
   this._onEditorUnload = this._onEditorUnload.bind(this);
   this._onEditorSelection = this._onEditorSelection.bind(this);
   this._onEditorContextMenu = this._onEditorContextMenu.bind(this);
+  this._onEditorContextMenuPopupHidden = this._onEditorContextMenuPopupHidden.bind(this);
   this._onBreakpointClick = this._onBreakpointClick.bind(this);
   this._onCheckboxClick = this._onCheckboxClick.bind(this);
   this._onConditionalPopupShowing = this._onConditionalPopupShowing.bind(this);
   this._onConditionalPopupShown = this._onConditionalPopupShown.bind(this);
   this._onConditionalPopupHiding = this._onConditionalPopupHiding.bind(this);
   this._onConditionalTextboxKeyPress = this._onConditionalTextboxKeyPress.bind(this);
 }
 
@@ -169,46 +170,51 @@ create({ constructor: BreakpointsView, p
   /**
    * Initialization function, called when the debugger is started.
    */
   initialize: function DVB_initialize() {
     dumpn("Initializing the BreakpointsView");
     this._container = new StackList(document.getElementById("breakpoints"));
     this._commandset = document.getElementById("debuggerCommands");
     this._popupset = document.getElementById("debuggerPopupset");
+    this._cmPopup = document.getElementById("sourceEditorContextMenu");
     this._cbPanel = document.getElementById("conditional-breakpoint-panel");
     this._cbTextbox = document.getElementById("conditional-breakpoint-textbox");
 
     this._container.emptyText = L10N.getStr("emptyBreakpointsText");
     this._container.itemFactory = this._createItemView;
     this._container.uniquenessQualifier = 2;
 
     window.addEventListener("Debugger:EditorLoaded", this._onEditorLoad, false);
     window.addEventListener("Debugger:EditorUnloaded", this._onEditorUnload, false);
     this._container.addEventListener("click", this._onBreakpointClick, false);
-    this._cbPanel.addEventListener("popupshowing", this._onConditionalPopupShowing, false)
-    this._cbPanel.addEventListener("popupshown", this._onConditionalPopupShown, false)
-    this._cbPanel.addEventListener("popuphiding", this._onConditionalPopupHiding, false)
+    this._cmPopup.addEventListener("popuphidden", this._onEditorContextMenuPopupHidden, false);
+    this._cbPanel.addEventListener("popupshowing", this._onConditionalPopupShowing, false);
+    this._cbPanel.addEventListener("popupshown", this._onConditionalPopupShown, false);
+    this._cbPanel.addEventListener("popuphiding", this._onConditionalPopupHiding, false);
     this._cbTextbox.addEventListener("keypress", this._onConditionalTextboxKeyPress, false);
 
     this._cache = new Map();
   },
 
   /**
    * Destruction function, called when the debugger is closed.
    */
   destroy: function DVB_destroy() {
     dumpn("Destroying the BreakpointsView");
     window.removeEventListener("Debugger:EditorLoaded", this._onEditorLoad, false);
     window.removeEventListener("Debugger:EditorUnloaded", this._onEditorUnload, false);
     this._container.removeEventListener("click", this._onBreakpointClick, false);
+    this._cmPopup.removeEventListener("popuphidden", this._onEditorContextMenuPopupHidden, false);
     this._cbPanel.removeEventListener("popupshowing", this._onConditionalPopupShowing, false);
     this._cbPanel.removeEventListener("popupshown", this._onConditionalPopupShown, false);
-    this._cbPanel.removeEventListener("popuphiding", this._onConditionalPopupHiding, false)
+    this._cbPanel.removeEventListener("popuphiding", this._onConditionalPopupHiding, false);
     this._cbTextbox.removeEventListener("keypress", this._onConditionalTextboxKeyPress, false);
+
+    this._cbPanel.hidePopup();
   },
 
   /**
    * Adds a breakpoint in this breakpoints container.
    *
    * @param string aSourceLocation
    *        The breakpoint source location specified by the debugger controller.
    * @param number aLineNumber
@@ -634,16 +640,23 @@ create({ constructor: BreakpointsView, p
    */
   _onEditorContextMenu: function DVB__onEditorContextMenu({ x, y }) {
     let offset = DebuggerView.editor.getOffsetAtLocation(x, y);
     let line = DebuggerView.editor.getLineAtOffset(offset);
     this._editorContextMenuLineNumber = line;
   },
 
   /**
+   * The context menu popup hidden listener for the source editor.
+   */
+  _onEditorContextMenuPopupHidden: function DVB__onEditorContextMenuPopupHidden() {
+    this._editorContextMenuLineNumber = -1;
+  },
+
+  /**
    * Called when the add breakpoint key sequence was pressed.
    */
   _onCmdAddBreakpoint: function BP__onCmdAddBreakpoint() {
     // If this command was executed via the context menu, add the breakpoint
     // on the currently hovered line in the source editor.
     if (this._editorContextMenuLineNumber >= 0) {
       DebuggerView.editor.setCaretPosition(this._editorContextMenuLineNumber);
     }
@@ -913,16 +926,17 @@ create({ constructor: BreakpointsView, p
    * @return string
    */
   _key: function DVB__key(aSourceLocation, aLineNumber) {
     return aSourceLocation + aLineNumber;
   },
 
   _popupset: null,
   _commandset: null,
+  _cmPopup: null,
   _cbPanel: null,
   _cbTextbox: null,
   _popupShown: false,
   _cache: null,
   _editorContextMenuLineNumber: -1
 });
 
 /**
@@ -1266,29 +1280,60 @@ create({ constructor: GlobalSearchView, 
       this._currentlyFocusedMatch = totalLineResults - 1;
     }
     this._onMatchClick({
       target: LineResults.getElementAtIndex(this._currentlyFocusedMatch)
     });
   },
 
   /**
+   * Allows searches to be scheduled and delayed to avoid redundant calls.
+   */
+  delayedSearch: true,
+
+  /**
    * Schedules searching for a string in all of the sources.
+   *
+   * @param string aQuery
+   *        The string to search for.
    */
-  scheduleSearch: function DVGS_scheduleSearch() {
+  scheduleSearch: function DVGS_scheduleSearch(aQuery) {
+    if (!this.delayedSearch) {
+      this.performSearch(aQuery);
+      return;
+    }
+    let delay = Math.max(GLOBAL_SEARCH_ACTION_MAX_DELAY / aQuery.length);
+
     window.clearTimeout(this._searchTimeout);
-    this._searchTimeout = window.setTimeout(this._startSearch, GLOBAL_SEARCH_ACTION_DELAY);
+    this._searchFunction = this._startSearch.bind(this, aQuery);
+    this._searchTimeout = window.setTimeout(this._searchFunction, delay);
+  },
+
+  /**
+   * Immediately searches for a string in all of the sources.
+   *
+   * @param string aQuery
+   *        The string to search for.
+   */
+  performSearch: function DVGS_performSearch(aQuery) {
+    window.clearTimeout(this._searchTimeout);
+    this._searchFunction = null;
+    this._startSearch(aQuery);
   },
 
   /**
    * Starts searching for a string in all of the sources.
+   *
+   * @param string aQuery
+   *        The string to search for.
    */
-  _startSearch: function DVGS__startSearch() {
+  _startSearch: function DVGS__startSearch(aQuery) {
     let locations = DebuggerView.Sources.values;
     this._sourcesCount = locations.length;
+    this._searchedToken = aQuery;
 
     this._fetchSources(
       this._onFetchSourceFinished,
       this._onFetchSourcesFinished, locations);
   },
 
   /**
    * Starts fetching all the sources, silently.
@@ -1344,17 +1389,17 @@ create({ constructor: GlobalSearchView, 
   },
 
   /**
    * Finds string matches in all the  sources stored in the cache, and groups
    * them by location and line number.
    */
   _performGlobalSearch: function DVGS__performGlobalSearch() {
     // Get the currently searched token from the filtering input.
-    let token = DebuggerView.Filtering.searchedToken;
+    let token = this._searchedToken;
 
     // Make sure we're actually searching for something.
     if (!token) {
       this.clearView();
       window.dispatchEvent("Debugger:GlobalSearch:TokenEmpty");
       return;
     }
 
@@ -1606,16 +1651,18 @@ create({ constructor: GlobalSearchView, 
       });
     }}, 0);
   },
 
   _splitter: null,
   _currentlyFocusedMatch: -1,
   _forceExpandResults: false,
   _searchTimeout: null,
+  _searchFunction: null,
+  _searchedToken: "",
   _sourcesCount: -1,
   _cache: null
 });
 
 /**
  * An object containing all source results, grouped by source location.
  * Iterable via "for (let [location, sourceResults] in globalResults) { }".
  */
@@ -1703,16 +1750,21 @@ SourceResults.prototype = {
   toggle: function SR_toggle(e) {
     if (e instanceof Event) {
       this._userToggled = true;
     }
     this.expanded ^= 1;
   },
 
   /**
+   * Relaxes the auto-expand rules to always show as many results as possible.
+   */
+  alwaysExpand: true,
+
+  /**
    * Gets this element's expanded state.
    * @return boolean
    */
   get expanded() this._target.resultsContainer.hasAttribute("open"),
 
   /**
    * Sets this element's expanded state.
    * @param boolean aFlag
@@ -1776,17 +1828,18 @@ SourceResults.prototype = {
     for (let [lineNumber, lineResults] of this._store) {
       lineResults.createView(resultsContainer, lineNumber, aCallbacks)
     }
 
     aElementNode.arrow = arrow;
     aElementNode.resultsHeader = resultsHeader;
     aElementNode.resultsContainer = resultsContainer;
 
-    if (aExpandFlag && aMatchCount < GLOBAL_SEARCH_EXPAND_MAX_RESULTS) {
+    if ((aExpandFlag || this.alwaysExpand) &&
+         aMatchCount < GLOBAL_SEARCH_EXPAND_MAX_RESULTS) {
       this.expand();
     }
 
     let resultsBox = document.createElement("vbox");
     resultsBox.setAttribute("flex", "1");
     resultsBox.appendChild(resultsHeader);
     resultsBox.appendChild(resultsContainer);
 
--- a/browser/devtools/debugger/debugger-toolbar.js
+++ b/browser/devtools/debugger/debugger-toolbar.js
@@ -816,44 +816,49 @@ FilterView.prototype = {
    */
   _onSearch: function DVF__onScriptsSearch() {
     this._searchboxPanel.hidePopup();
     let [file, line, token, isGlobal, isVariable] = this.searchboxInfo;
 
     // If this is a global search, schedule it for when the user stops typing,
     // or hide the corresponding pane otherwise.
     if (isGlobal) {
-      DebuggerView.GlobalSearch.scheduleSearch();
+      DebuggerView.GlobalSearch.scheduleSearch(token);
       return;
     }
 
     // If this is a variable search, defer the action to the corresponding
     // variables view instance.
     if (isVariable) {
-      DebuggerView.Variables.performSearch(token);
+      DebuggerView.Variables.scheduleSearch(token);
       return;
     }
 
     DebuggerView.GlobalSearch.clearView();
     this._performFileSearch(file);
     this._performLineSearch(line);
     this._performTokenSearch(token);
   },
 
   /**
    * The key press listener for the search container.
    */
   _onKeyPress: function DVF__onScriptsKeyPress(e) {
     let [file, line, token, isGlobal, isVariable] = this.searchboxInfo;
-    let action;
+    let isDifferentToken, isReturnKey, action;
 
+    if (this._prevSearchedToken != token) {
+      isDifferentToken = true;
+    }
     switch (e.keyCode) {
-      case e.DOM_VK_DOWN:
       case e.DOM_VK_RETURN:
       case e.DOM_VK_ENTER:
+        isReturnKey = true;
+        // fall through
+      case e.DOM_VK_DOWN:
         action = 0;
         break;
       case e.DOM_VK_UP:
         action = 1;
         break;
       case e.DOM_VK_ESCAPE:
         action = 2;
         break;
@@ -869,28 +874,32 @@ FilterView.prototype = {
       return;
     }
 
     e.preventDefault();
     e.stopPropagation();
 
     // Perform a global search based on the specified operator.
     if (isGlobal) {
-      if (DebuggerView.GlobalSearch.hidden) {
-        DebuggerView.GlobalSearch.scheduleSearch();
+      if (isReturnKey && isDifferentToken) {
+        DebuggerView.GlobalSearch.performSearch(token);
       } else {
         DebuggerView.GlobalSearch[["focusNextMatch", "focusPrevMatch"][action]]();
       }
+      this._prevSearchedToken = token;
       return;
     }
 
     // Perform a variable search based on the specified operator.
     if (isVariable) {
-      DebuggerView.Variables.performSearch(token);
-      DebuggerView.Variables.expandFirstSearchResults();
+      if (isReturnKey && isDifferentToken) {
+        DebuggerView.Variables.performSearch(token);
+        DebuggerView.Variables.expandFirstSearchResults();
+      }
+      this._prevSearchedToken = token;
       return;
     }
 
     let editor = DebuggerView.editor;
     let offset = editor[["findNext", "findPrevious"][action]](true);
     if (offset > -1) {
       editor.setSelection(offset, offset + token.length)
     }
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -8,17 +8,17 @@
 const SOURCE_URL_MAX_LENGTH = 64; // chars
 const SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE = 1048576; // 1 MB in bytes
 const PANES_APPEARANCE_DELAY = 50; // ms
 const BREAKPOINT_LINE_TOOLTIP_MAX_LENGTH = 1000; // chars
 const BREAKPOINT_CONDITIONAL_POPUP_POSITION = "after_start";
 const BREAKPOINT_CONDITIONAL_POPUP_OFFSET = 50; // px
 const GLOBAL_SEARCH_LINE_MAX_LENGTH = 300; // chars
 const GLOBAL_SEARCH_EXPAND_MAX_RESULTS = 50;
-const GLOBAL_SEARCH_ACTION_DELAY = 150; // ms
+const GLOBAL_SEARCH_ACTION_MAX_DELAY = 1500; // ms
 const SEARCH_GLOBAL_FLAG = "!";
 const SEARCH_LINE_FLAG = ":";
 const SEARCH_TOKEN_FLAG = "#";
 const SEARCH_VARIABLE_FLAG = "*";
 
 /**
  * Object defining the debugger view components.
  */
--- a/browser/devtools/debugger/test/browser_dbg_bug727429_watch-expressions-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_bug727429_watch-expressions-02.js
@@ -37,26 +37,27 @@ function test()
     gWatch.addExpression("\"a''\"");
     gWatch.addExpression("?");
     gWatch.addExpression("a");
     gWatch.addExpression("[1, 2, 3]");
     gWatch.addExpression("x = [1, 2, 3]");
     gWatch.addExpression("y = [1, 2, 3]; y.test = 4");
     gWatch.addExpression("z = [1, 2, 3]; z.test = 4; z");
     gWatch.addExpression("t = [1, 2, 3]; t.test = 4; !t");
+    gWatch.addExpression("arguments[0]");
     gWatch.addExpression("encodeURI(\"\\\")");
     gWatch.addExpression("decodeURI(\"\\\")");
   }
 
   function performTest()
   {
     is(gWatch._container._parent.querySelectorAll(".dbg-expression[hidden=true]").length, 0,
       "There should be 0 hidden nodes in the watch expressions container");
-    is(gWatch._container._parent.querySelectorAll(".dbg-expression:not([hidden=true])").length, 13,
-      "There should be 13 visible nodes in the watch expressions container");
+    is(gWatch._container._parent.querySelectorAll(".dbg-expression:not([hidden=true])").length, 14,
+      "There should be 14 visible nodes in the watch expressions container");
 
     test1(function() {
       test2(function() {
         test3(function() {
           test4(function() {
             test5(function() {
               test6(function() {
                 test7(function() {
@@ -73,107 +74,107 @@ function test()
       });
     });
   }
 
   function finishTest()
   {
     is(gWatch._container._parent.querySelectorAll(".dbg-expression[hidden=true]").length, 0,
       "There should be 0 hidden nodes in the watch expressions container");
-    is(gWatch._container._parent.querySelectorAll(".dbg-expression:not([hidden=true])").length, 12,
-      "There should be 12 visible nodes in the watch expressions container");
+    is(gWatch._container._parent.querySelectorAll(".dbg-expression:not([hidden=true])").length, 13,
+      "There should be 13 visible nodes in the watch expressions container");
 
     closeDebuggerAndFinish();
   }
 
   function test1(callback) {
     waitForWatchExpressions(function() {
       info("Performing test1");
-      checkWatchExpressions("ReferenceError: a is not defined");
+      checkWatchExpressions("ReferenceError: a is not defined", undefined);
       callback();
     });
     executeSoon(function() {
       gDebuggee.ermahgerd(); // ermahgerd!!
     });
   }
 
   function test2(callback) {
     waitForWatchExpressions(function() {
       info("Performing test2");
-      checkWatchExpressions(undefined);
+      checkWatchExpressions(undefined, "sensational");
       callback();
     });
     EventUtils.sendMouseEvent({ type: "mousedown" },
       gDebugger.document.getElementById("resume"),
       gDebugger);
   }
 
   function test3(callback) {
     waitForWatchExpressions(function() {
       info("Performing test3");
-      checkWatchExpressions({ type: "object", class: "Object" });
+      checkWatchExpressions({ type: "object", class: "Object" }, "sensational");
       callback();
     });
     EventUtils.sendMouseEvent({ type: "mousedown" },
       gDebugger.document.getElementById("resume"),
       gDebugger);
   }
 
   function test4(callback) {
     waitForWatchExpressions(function() {
       info("Performing test4");
-      checkWatchExpressions(5, 12);
+      checkWatchExpressions(5, "sensational", 13);
       callback();
     });
     executeSoon(function() {
       gWatch.addExpression("a = 5");
       EventUtils.sendKey("RETURN");
     });
   }
 
   function test5(callback) {
     waitForWatchExpressions(function() {
       info("Performing test5");
-      checkWatchExpressions(5, 12);
+      checkWatchExpressions(5, "sensational", 13);
       callback();
     });
     executeSoon(function() {
       gWatch.addExpression("encodeURI(\"\\\")");
       EventUtils.sendKey("RETURN");
     });
   }
 
   function test6(callback) {
     waitForWatchExpressions(function() {
       info("Performing test6");
-      checkWatchExpressions(5, 12);
+      checkWatchExpressions(5, "sensational", 13);
       callback();
     })
     executeSoon(function() {
       gWatch.addExpression("decodeURI(\"\\\")");
       EventUtils.sendKey("RETURN");
     });
   }
 
   function test7(callback) {
     waitForWatchExpressions(function() {
       info("Performing test7");
-      checkWatchExpressions(5, 12);
+      checkWatchExpressions(5, "sensational", 13);
       callback();
     });
     executeSoon(function() {
       gWatch.addExpression("?");
       EventUtils.sendKey("RETURN");
     });
   }
 
   function test8(callback) {
     waitForWatchExpressions(function() {
       info("Performing test8");
-      checkWatchExpressions(5, 12);
+      checkWatchExpressions(5, "sensational", 13);
       callback();
     });
     executeSoon(function() {
       gWatch.addExpression("a");
       EventUtils.sendKey("RETURN");
     });
   }
 
@@ -196,17 +197,17 @@ function test()
 
   function waitForWatchExpressions(callback) {
     gDebugger.addEventListener("Debugger:FetchedWatchExpressions", function onFetch() {
       gDebugger.removeEventListener("Debugger:FetchedWatchExpressions", onFetch, false);
       executeSoon(callback);
     }, false);
   }
 
-  function checkWatchExpressions(expected, total = 11) {
+  function checkWatchExpressions(expected_a, expected_arguments, total = 12) {
     is(gWatch._container._parent.querySelectorAll(".dbg-expression[hidden=true]").length, total,
       "There should be " + total + " hidden nodes in the watch expressions container");
     is(gWatch._container._parent.querySelectorAll(".dbg-expression:not([hidden=true])").length, 0,
       "There should be 0 visible nodes in the watch expressions container");
 
     let label = gDebugger.L10N.getStr("watchExpressionsScopeLabel");
     let scope = gVars._currHierarchy.get(label);
 
@@ -218,54 +219,54 @@ function test()
     let w3 = scope.get("'a\"\"'");
     let w4 = scope.get("\"a''\"");
     let w5 = scope.get("?");
     let w6 = scope.get("a");
     let w7 = scope.get("x = [1, 2, 3]");
     let w8 = scope.get("y = [1, 2, 3]; y.test = 4");
     let w9 = scope.get("z = [1, 2, 3]; z.test = 4; z");
     let w10 = scope.get("t = [1, 2, 3]; t.test = 4; !t");
-    let w11 = scope.get("encodeURI(\"\\\")");
-    let w12 = scope.get("decodeURI(\"\\\")");
+    let w11 = scope.get("arguments[0]");
+    let w12 = scope.get("encodeURI(\"\\\")");
+    let w13 = scope.get("decodeURI(\"\\\")");
 
     ok(w1, "The first watch expression should be present in the scope");
     ok(w2, "The second watch expression should be present in the scope");
     ok(w3, "The third watch expression should be present in the scope");
     ok(w4, "The fourth watch expression should be present in the scope");
     ok(w5, "The fifth watch expression should be present in the scope");
     ok(w6, "The sixth watch expression should be present in the scope");
     ok(w7, "The seventh watch expression should be present in the scope");
     ok(w8, "The eight watch expression should be present in the scope");
     ok(w9, "The ninth watch expression should be present in the scope");
     ok(w10, "The tenth watch expression should be present in the scope");
-    ok(!w11, "The eleventh watch expression should not be present in the scope");
+    ok(w11, "The eleventh watch expression should be present in the scope");
     ok(!w12, "The twelveth watch expression should not be present in the scope");
+    ok(!w13, "The thirteenth watch expression should not be present in the scope");
 
     is(w1.value, "a", "The first value is correct");
     is(w2.value, "a", "The second value is correct");
     is(w3.value, "a\"\"", "The third value is correct");
     is(w4.value, "a''", "The fourth value is correct");
     is(w5.value, "SyntaxError: syntax error", "The fifth value is correct");
 
-    if (typeof expected == "object") {
-      is(w6.value.type, expected.type, "The sixth value type is correct");
-      is(w6.value.class, expected.class, "The sixth value class is correct");
+    if (typeof expected_a == "object") {
+      is(w6.value.type, expected_a.type, "The sixth value type is correct");
+      is(w6.value.class, expected_a.class, "The sixth value class is correct");
     } else {
-      is(w6.value, expected, "The sixth value is correct");
+      is(w6.value, expected_a, "The sixth value is correct");
     }
 
     is(w7.value.type, "object", "The seventh value type is correct");
     is(w7.value.class, "Array", "The seventh value class is correct");
-
     is(w8.value, "4", "The eight value is correct");
-
     is(w9.value.type, "object", "The ninth value type is correct");
     is(w9.value.class, "Array", "The ninth value class is correct");
-
     is(w10.value, false, "The tenth value is correct");
+    is(w11.value, expected_arguments, "The eleventh value is correct");
   }
 
   registerCleanupFunction(function() {
     removeTab(gTab);
     gPane = null;
     gTab = null;
     gDebuggee = null;
     gDebugger = null;
--- a/browser/devtools/debugger/test/browser_dbg_bug740825_conditional-breakpoints-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_bug740825_conditional-breakpoints-02.js
@@ -169,16 +169,91 @@ function test()
     EventUtils.sendKey("RETURN");
 
     waitForBreakpoint(14, function() {
       waitForCaretPos(13, function() {
         waitForPopup(false, function() {
           is(gBreakpointsPane.selectedClient.conditionalExpression, "bamboocha",
             "The bamboocha expression wasn't fonud on the conditional breakpoint");
 
+          executeSoon(setContextMenu);
+        });
+      });
+    });
+  }
+
+  function setContextMenu()
+  {
+    let contextMenu = gDebugger.document.getElementById("sourceEditorContextMenu");
+
+    contextMenu.addEventListener("popupshown", function onPopupShown() {
+      contextMenu.removeEventListener("popupshown", onPopupShown, false);
+
+      contextMenu.addEventListener("popuphidden", function onPopupHidden() {
+        contextMenu.removeEventListener("popuphidden", onPopupHidden, false);
+
+        executeSoon(addBreakpoint4);
+      }, false);
+
+      gBreakpointsPane._editorContextMenuLineNumber = 0;
+      contextMenu.hidePopup();
+    }, false);
+
+    gBreakpointsPane._editorContextMenuLineNumber = 14;
+    contextMenu.openPopup(gEditor.editorElement, "overlap", 0, 0, true, false);
+  }
+
+  function addBreakpoint4()
+  {
+    gEditor.setCaretPosition(14);
+    gBreakpointsPane._onCmdAddBreakpoint();
+
+    waitForBreakpoint(15, function() {
+      waitForCaretPos(14, function() {
+        waitForPopup(false, function() {
+          testBreakpoint(gBreakpointsPane.selectedItem,
+                         gBreakpointsPane.selectedClient,
+                         gScripts.selectedValue, 15, false, false, true);
+
+          executeSoon(delBreakpoint4);
+        });
+      });
+    });
+  }
+
+  function delBreakpoint4()
+  {
+    gEditor.setCaretPosition(14);
+    gBreakpointsPane._onCmdAddBreakpoint();
+
+    waitForBreakpoint(null, function() {
+      waitForCaretPos(14, function() {
+        waitForPopup(false, function() {
+          is(gBreakpointsPane.selectedItem, null,
+            "There should be no selected breakpoint in the breakpoints pane.")
+          is(gBreakpointsPane._popupShown, false,
+            "The breakpoint conditional expression popup should not be shown.");
+
+          executeSoon(moveHighlight1);
+        });
+      });
+    });
+  }
+
+  function moveHighlight1()
+  {
+    gEditor.setCaretPosition(13);
+
+    waitForBreakpoint(14, function() {
+      waitForCaretPos(13, function() {
+        waitForPopup(false, function() {
+          testBreakpoint(gBreakpointsPane.selectedItem,
+                         gBreakpointsPane.selectedClient,
+                         gScripts.selectedValue, 14, true, false, true);
+
           executeSoon(testHighlights1);
         });
       });
     });
   }
 
   function testHighlights1()
   {
@@ -383,18 +458,18 @@ function test()
     is(aBreakpointItem.attachment.sourceLocation, gScripts.selectedValue,
       "The breakpoint on line " + line + " wasn't added on the correct source.");
     is(aBreakpointItem.attachment.lineNumber, line,
       "The breakpoint on line " + line + " wasn't found.");
     is(aBreakpointItem.attachment.enabled, true,
       "The breakpoint on line " + line + " should be enabled.");
     is(aBreakpointItem.attachment.isConditional, conditional,
       "The breakpoint on line " + line + " should " + (conditional ? "" : "not ") + "be conditional.");
-    is(gBreakpointsPane._popupShown, conditional,
-      "The breakpoint conditional expression popup should" + (conditional ? "" : "not ") + "be shown.");
+    is(gBreakpointsPane._popupShown, popup,
+      "The breakpoint conditional expression popup should" + (popup ? "" : "not ") + "be shown.");
 
     is(aBreakpointClient.location.url, url,
        "The breakpoint's client url is correct");
     is(aBreakpointClient.location.line, line,
        "The breakpoint's client line is correct");
 
     if (conditional) {
       isnot(aBreakpointClient.conditionalExpression, undefined,
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-filter-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-filter-01.js
@@ -20,16 +20,17 @@ function test()
 {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
     gDebuggee = aDebuggee;
 
     gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
+    gDebugger.DebuggerView.Variables.delayedSearch = false;
     testSearchbox();
     prepareVariables(testVariablesFiltering);
   });
 }
 
 function testSearchbox()
 {
   ok(!gDebugger.DebuggerView.Variables._searchboxNode,
@@ -344,20 +345,20 @@ function prepareVariables(aCallback)
         "The mathScope expanded getter should return false");
       is(testScopeItem.expanded, false,
         "The testScope expanded getter should return false");
       is(loadScopeItem.expanded, false,
         "The loadScope expanded getter should return false");
       is(globalScopeItem.expanded, false,
         "The globalScope expanded getter should return false");
 
-      EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebuggee);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebugger);
 
       is(innerScopeItem.expanded, true,
         "The innerScope expanded getter should return true");
       is(mathScopeItem.expanded, true,
         "The mathScope expanded getter should return true");
       is(testScopeItem.expanded, true,
         "The testScope expanded getter should return true");
       is(loadScopeItem.expanded, true,
@@ -411,50 +412,50 @@ function prepareVariables(aCallback)
                         aCallback();
                       });
                     }}, 0);
                   }, false);
 
                   executeSoon(function() {
                     EventUtils.sendMouseEvent({ type: "mousedown" },
                       locationItem.target.querySelector(".arrow"),
-                      gDebuggee.window);
+                      gDebugger);
 
                     is(locationItem.expanded, true,
                       "The local scope 'this.window.document.location' should be expanded now");
                   });
                 }}, 0);
               }, false);
 
               executeSoon(function() {
                 EventUtils.sendMouseEvent({ type: "mousedown" },
                   documentItem.target.querySelector(".arrow"),
-                  gDebuggee.window);
+                  gDebugger);
 
                 is(documentItem.expanded, true,
                   "The local scope 'this.window.document' should be expanded now");
               });
             }}, 0);
           }, false);
 
           executeSoon(function() {
             EventUtils.sendMouseEvent({ type: "mousedown" },
               windowItem.target.querySelector(".arrow"),
-              gDebuggee.window);
+              gDebugger);
 
             is(windowItem.expanded, true,
               "The local scope 'this.window' should be expanded now");
           });
         }}, 0);
       }, false);
 
       executeSoon(function() {
         EventUtils.sendMouseEvent({ type: "mousedown" },
           thisItem.target.querySelector(".arrow"),
-          gDebuggee.window);
+          gDebugger);
 
         is(thisItem.expanded, true,
           "The local scope 'this' should be expanded now");
       });
     }}, 0);
   }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-filter-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-filter-02.js
@@ -20,16 +20,17 @@ function test()
 {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
     gDebuggee = aDebuggee;
 
     gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
+    gDebugger.DebuggerView.Variables.delayedSearch = false;
     testSearchbox();
     prepareVariables(testVariablesFiltering);
   });
 }
 
 function testSearchbox()
 {
   ok(!gDebugger.DebuggerView.Variables._searchboxNode,
@@ -306,20 +307,20 @@ function prepareVariables(aCallback)
         "The mathScope expanded getter should return false");
       is(testScopeItem.expanded, false,
         "The testScope expanded getter should return false");
       is(loadScopeItem.expanded, false,
         "The loadScope expanded getter should return false");
       is(globalScopeItem.expanded, false,
         "The globalScope expanded getter should return false");
 
-      EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebuggee);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebugger);
 
       is(innerScopeItem.expanded, true,
         "The innerScope expanded getter should return true");
       is(mathScopeItem.expanded, true,
         "The mathScope expanded getter should return true");
       is(testScopeItem.expanded, true,
         "The testScope expanded getter should return true");
       is(loadScopeItem.expanded, true,
@@ -373,50 +374,50 @@ function prepareVariables(aCallback)
                         aCallback();
                       });
                     }}, 0);
                   }, false);
 
                   executeSoon(function() {
                     EventUtils.sendMouseEvent({ type: "mousedown" },
                       locationItem.target.querySelector(".arrow"),
-                      gDebuggee.window);
+                      gDebugger);
 
                     is(locationItem.expanded, true,
                       "The local scope 'this.window.document.location' should be expanded now");
                   });
                 }}, 0);
               }, false);
 
               executeSoon(function() {
                 EventUtils.sendMouseEvent({ type: "mousedown" },
                   documentItem.target.querySelector(".arrow"),
-                  gDebuggee.window);
+                  gDebugger);
 
                 is(documentItem.expanded, true,
                   "The local scope 'this.window.document' should be expanded now");
               });
             }}, 0);
           }, false);
 
           executeSoon(function() {
             EventUtils.sendMouseEvent({ type: "mousedown" },
               windowItem.target.querySelector(".arrow"),
-              gDebuggee.window);
+              gDebugger);
 
             is(windowItem.expanded, true,
               "The local scope 'this.window' should be expanded now");
           });
         }}, 0);
       }, false);
 
       executeSoon(function() {
         EventUtils.sendMouseEvent({ type: "mousedown" },
           thisItem.target.querySelector(".arrow"),
-          gDebuggee.window);
+          gDebugger);
 
         is(thisItem.expanded, true,
           "The local scope 'this' should be expanded now");
       });
     }}, 0);
   }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-filter-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-filter-05.js
@@ -20,16 +20,17 @@ function test()
 {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
     gDebuggee = aDebuggee;
 
     gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
+    gDebugger.DebuggerView.Variables.delayedSearch = false;
     prepareVariables(testVariablesFiltering);
   });
 }
 
 function testVariablesFiltering()
 {
   function test1()
   {
@@ -238,20 +239,20 @@ function prepareVariables(aCallback)
         mathScope.querySelector(".name").getAttribute("value"));
       let testScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
         testScope.querySelector(".name").getAttribute("value"));
       let loadScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
         loadScope.querySelector(".name").getAttribute("value"));
       let globalScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
         globalScope.querySelector(".name").getAttribute("value"));
 
-      EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebuggee);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebugger);
 
       executeSoon(function() {
         aCallback();
       });
     }}, 0);
   }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-filter-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-filter-06.js
@@ -20,16 +20,17 @@ function test()
 {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
     gDebuggee = aDebuggee;
 
     gDebugger.DebuggerController.StackFrames.autoScopeExpand = false;
+    gDebugger.DebuggerView.Variables.delayedSearch = false;
     prepareVariables(testVariablesFiltering);
   });
 }
 
 function testVariablesFiltering()
 {
   let f = {
     test1: function()
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-filter-07.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-filter-07.js
@@ -20,16 +20,17 @@ function test()
 {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
     gDebuggee = aDebuggee;
 
     gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
+    gDebugger.DebuggerView.Variables.delayedSearch = false;
     prepareVariables(testVariablesFiltering);
   });
 }
 
 function testVariablesFiltering()
 {
   let f = {
     test1: function()
@@ -198,20 +199,20 @@ function prepareVariables(aCallback)
         mathScope.querySelector(".name").getAttribute("value"));
       let testScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
         testScope.querySelector(".name").getAttribute("value"));
       let loadScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
         loadScope.querySelector(".name").getAttribute("value"));
       let globalScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
         globalScope.querySelector(".name").getAttribute("value"));
 
-      EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebuggee);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebugger);
 
       executeSoon(function() {
         aCallback();
       });
     }}, 0);
   }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-filter-08.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-filter-08.js
@@ -20,16 +20,17 @@ function test()
 {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
     gDebuggee = aDebuggee;
 
     gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
+    gDebugger.DebuggerView.Variables.delayedSearch = false;
     prepareVariables(testVariablesFiltering);
   });
 }
 
 function testVariablesFiltering()
 {
   let f = {
     test1: function(aCallback)
@@ -268,20 +269,20 @@ function prepareVariables(aCallback)
         mathScope.querySelector(".name").getAttribute("value"));
       let testScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
         testScope.querySelector(".name").getAttribute("value"));
       let loadScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
         loadScope.querySelector(".name").getAttribute("value"));
       let globalScopeItem = gDebugger.DebuggerView.Variables._currHierarchy.get(
         globalScope.querySelector(".name").getAttribute("value"));
 
-      EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebuggee);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebugger);
 
       executeSoon(function() {
         aCallback();
       });
     }}, 0);
   }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-reexpand.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-reexpand.js
@@ -142,20 +142,20 @@ function testVariablesExpand()
       is(testScopeItem.expanded, false,
         "The testScope expanded getter should return false");
       is(loadScopeItem.expanded, false,
         "The loadScope expanded getter should return false");
       is(globalScopeItem.expanded, false,
         "The globalScope expanded getter should return false");
 
 
-      EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebuggee);
-      EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebuggee);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, mathScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, testScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, loadScope.querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, globalScope.querySelector(".arrow"), gDebugger);
 
 
       is(innerScope.querySelector(".arrow").hasAttribute("open"), true,
         "The innerScope arrow should now be expanded");
       is(mathScope.querySelector(".arrow").hasAttribute("open"), true,
         "The mathScope arrow should now be expanded");
       is(testScope.querySelector(".arrow").hasAttribute("open"), true,
         "The testScope arrow should now be expanded");
@@ -322,58 +322,58 @@ function testVariablesExpand()
                             closeDebuggerAndFinish();
                           });
                         }}, 0);
                       }, false);
 
                       executeSoon(function() {
                         EventUtils.sendMouseEvent({ type: "mousedown" },
                           gDebugger.document.querySelector("#step-in"),
-                          gDebugger.window);
+                          gDebugger);
                       });
                     }}, 0);
                   }, false);
 
                   executeSoon(function() {
                     EventUtils.sendMouseEvent({ type: "mousedown" },
                       locationItem.target.querySelector(".arrow"),
-                      gDebuggee.window);
+                      gDebugger);
 
                     is(locationItem.expanded, true,
                       "The local scope 'this.window.document.location' should be expanded now");
                   });
                 }}, 0);
               }, false);
 
               executeSoon(function() {
                 EventUtils.sendMouseEvent({ type: "mousedown" },
                   documentItem.target.querySelector(".arrow"),
-                  gDebuggee.window);
+                  gDebugger);
 
                 is(documentItem.expanded, true,
                   "The local scope 'this.window.document' should be expanded now");
               });
             }}, 0);
           }, false);
 
           executeSoon(function() {
             EventUtils.sendMouseEvent({ type: "mousedown" },
               windowItem.target.querySelector(".arrow"),
-              gDebuggee.window);
+              gDebugger);
 
             is(windowItem.expanded, true,
               "The local scope 'this.window' should be expanded now");
           });
         }}, 0);
       }, false);
 
       executeSoon(function() {
         EventUtils.sendMouseEvent({ type: "mousedown" },
           thisItem.target.querySelector(".arrow"),
-          gDebuggee.window);
+          gDebugger);
 
         is(thisItem.expanded, true,
           "The local scope 'this' should be expanded now");
       });
     }}, 0);
   }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
--- a/browser/devtools/debugger/test/browser_dbg_scripts-searching-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-searching-01.js
@@ -20,16 +20,17 @@ function test()
   let scriptShown = false;
   let framesAdded = false;
 
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
+    gDebugger.SourceResults.prototype.alwaysExpand = false;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.simpleCall();
   });
--- a/browser/devtools/debugger/test/browser_dbg_scripts-searching-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-searching-02.js
@@ -22,16 +22,17 @@ function test()
   let scriptShown = false;
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
+    gDebugger.SourceResults.prototype.alwaysExpand = false;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.firstCall();
   });
--- a/browser/devtools/debugger/test/browser_dbg_scripts-searching-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-searching-03.js
@@ -23,16 +23,17 @@ function test()
   let scriptShown = false;
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
+    gDebugger.SourceResults.prototype.alwaysExpand = false;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.firstCall();
   });
--- a/browser/devtools/debugger/test/browser_dbg_scripts-searching-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-searching-04.js
@@ -23,16 +23,17 @@ function test()
   let scriptShown = false;
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
+    gDebugger.SourceResults.prototype.alwaysExpand = false;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.firstCall();
   });
--- a/browser/devtools/debugger/test/browser_dbg_scripts-searching-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-searching-05.js
@@ -23,16 +23,17 @@ function test()
   let scriptShown = false;
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
+    gDebugger.SourceResults.prototype.alwaysExpand = false;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.firstCall();
   });
--- a/browser/devtools/debugger/test/browser_dbg_scripts-searching-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-searching-06.js
@@ -23,16 +23,17 @@ function test()
   let scriptShown = false;
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
+    gDebugger.SourceResults.prototype.alwaysExpand = false;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.firstCall();
   });
--- a/browser/devtools/debugger/test/browser_dbg_scripts-searching-07.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-searching-07.js
@@ -24,16 +24,17 @@ function test()
   let scriptShown = false;
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
+    gDebugger.SourceResults.prototype.alwaysExpand = false;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.firstCall();
   });
--- a/browser/devtools/debugger/test/browser_dbg_scripts-searching-08.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-searching-08.js
@@ -23,16 +23,17 @@ function test()
   let scriptShown = false;
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.contentWindow;
+    gDebugger.SourceResults.prototype.alwaysExpand = false;
 
     gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.firstCall();
   });
--- a/browser/devtools/debugger/test/browser_dbg_watch-expressions.html
+++ b/browser/devtools/debugger/test/browser_dbg_watch-expressions.html
@@ -8,15 +8,15 @@
     <script type="text/javascript">
       function ermahgerd() {
         debugger;
         (function() {
           var a = undefined;
           debugger;
           var a = {};
           debugger;
-        }());
+        }("sensational"));
       }
     </script>
   </head>
   <body>
   </body>
 </html>
--- a/browser/devtools/shared/VariablesView.jsm
+++ b/browser/devtools/shared/VariablesView.jsm
@@ -2,16 +2,17 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
 const LAZY_EMPTY_DELAY = 150; // ms
+const SEARCH_ACTION_MAX_DELAY = 1000; // ms
 
 Components.utils.import('resource://gre/modules/Services.jsm');
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 this.EXPORTED_SYMBOLS = ["VariablesView", "create"];
 
 /**
  * A tree view for inspecting scopes, objects and properties.
@@ -206,23 +207,58 @@ VariablesView.prototype = {
   set searchEnabled(aFlag) aFlag ? this.enableSearch() : this.disableSearch(),
 
   /**
    * Gets if the variable and property searching is enabled.
    */
   get searchEnabled() !!this._searchboxContainer,
 
   /**
+   * Allows searches to be scheduled and delayed to avoid redundant calls.
+   */
+  delayedSearch: true,
+
+  /**
+   * Schedules searching for variables or properties matching the query.
+   *
+   * @param string aQuery
+   *        The variable or property to search for.
+   */
+  scheduleSearch: function VV_scheduleSearch(aQuery) {
+    if (!this.delayedSearch) {
+      this.performSearch(aQuery);
+      return;
+    }
+    let delay = Math.max(SEARCH_ACTION_MAX_DELAY / aQuery.length, 0);
+
+    this.window.clearTimeout(this._searchTimeout);
+    this._searchFunction = this._startSearch.bind(this, aQuery);
+    this._searchTimeout = this.window.setTimeout(this._searchFunction, delay);
+  },
+
+  /**
+   * Immediately searches for variables or properties matching the query.
+   *
+   * @param string aQuery
+   *        The variable or property to search for.
+   */
+  performSearch: function VV_performSearch(aQuery) {
+    this.window.clearTimeout(this._searchTimeout);
+    this._searchFunction = null;
+    this._startSearch(aQuery);
+  },
+
+  /**
    * Performs a case insensitive search for variables or properties matching
    * the query, and hides non-matched items.
    *
    * @param string aQuery
    *        The variable or property to search for.
    */
-  performSearch: function VV_performSerch(aQuery) {
+  _startSearch: function VV__startSearch(aQuery) {
     for (let [, scope] in this) {
       switch (aQuery) {
         case "":
           scope.expand();
           // fall through
         case null:
           scope._performSearch("");
           break;
@@ -339,16 +375,18 @@ VariablesView.prototype = {
   get window() this.document.defaultView,
 
   eval: null,
   lazyEmpty: false,
   _store: null,
   _prevHierarchy: null,
   _currHierarchy: null,
   _emptyTimeout: null,
+  _searchTimeout: null,
+  _searchFunction: null,
   _enumVisible: true,
   _nonEnumVisible: true,
   _parent: null,
   _list: null,
   _searchboxNode: null,
   _searchboxContainer: null,
   _searchboxPlaceholder: "",
   _emptyTextNode: null,
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_580030_errors_after_page_reload.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_580030_errors_after_page_reload.js
@@ -30,17 +30,17 @@ function testErrorsAfterPageReload(aEven
 
   // dispatch a click event to the button in the test page and listen for
   // errors.
 
   Services.console.registerListener(consoleObserver);
 
   let button = content.document.querySelector("button").wrappedJSObject;
   ok(button, "button found");
-  EventUtils.sendMouseEvent({type: "click"}, button, content);
+  EventUtils.sendMouseEvent({type: "click"}, button, content.wrappedJSObject);
 }
 
 var consoleObserver = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   observe: function test_observe(aMessage)
   {
     // Ignore errors we don't care about.
--- a/browser/devtools/webconsole/test/browser_webconsole_view_source.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_view_source.js
@@ -15,17 +15,17 @@ function test() {
 }
 
 function testViewSource(hud) {
   let button = content.document.querySelector("button");
   button = XPCNativeWrapper.unwrap(button);
   ok(button, "we have the button on the page");
 
   expectUncaughtException();
-  EventUtils.sendMouseEvent({ type: "click" }, button, content);
+  EventUtils.sendMouseEvent({ type: "click" }, button, XPCNativeWrapper.unwrap(content));
 
   waitForSuccess({
     name: "find the location node",
     validatorFn: function()
     {
       return hud.outputNode.querySelector(".webconsole-location");
     },
     successFn: function()
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -160,16 +160,18 @@ const OUTPUT_INTERVAL = 50; // milliseco
 const THROTTLE_UPDATES = 1000; // milliseconds
 
 // The preference prefix for all of the Web Console filters.
 const FILTER_PREFS_PREFIX = "devtools.webconsole.filter.";
 
 // The minimum font size.
 const MIN_FONT_SIZE = 10;
 
+const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
+
 /**
  * A WebConsoleFrame instance is an interactive console initialized *per tab*
  * that displays console log data as well as provides an interactive terminal to
  * manipulate the current tab's document content.
  *
  * The WebConsoleFrame is responsible for the actual Web Console UI
  * implementation.
  *
@@ -186,16 +188,17 @@ function WebConsoleFrame(aWebConsoleOwne
   this._cssNodes = {};
   this._outputQueue = [];
   this._pruneCategoriesQueue = {};
   this._networkRequests = {};
 
   this._toggleFilter = this._toggleFilter.bind(this);
   this._onPositionConsoleCommand = this._onPositionConsoleCommand.bind(this);
   this._flushMessageQueue = this._flushMessageQueue.bind(this);
+  this._connectionTimeout = this._connectionTimeout.bind(this);
 
   this._outputTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   this._outputTimerInitialized = false;
 
   this._initDefaultFilterPrefs();
   this._commandController = new CommandController(this);
   this.positionConsole(aPosition, window);
 
@@ -218,16 +221,23 @@ WebConsoleFrame.prototype = {
    * the remote server, using the remote debugging protocol.
    *
    * @see WebConsoleConnectionProxy
    * @type object
    */
   proxy: null,
 
   /**
+   * Timer used for the connection.
+   * @private
+   * @type object
+   */
+  _connectTimer: null,
+
+  /**
    * Getter for the xul:popupset that holds any popups we open.
    * @type nsIDOMElement
    */
   get popupset() this.owner.mainPopupSet,
 
   /**
    * Holds the network requests currently displayed by the Web Console. Each key
    * represents the connection ID and the value is network request information.
@@ -363,23 +373,64 @@ WebConsoleFrame.prototype = {
    */
   _initConnection: function WCF__initConnection()
   {
     this.proxy = new WebConsoleConnectionProxy(this, {
       host: this.owner.remoteHost,
       port: this.owner.remotePort,
     });
 
+    let timeout = Services.prefs.getIntPref(PREF_CONNECTION_TIMEOUT);
+    this._connectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    this._connectTimer.initWithCallback(this._connectionTimeout,
+                                        timeout, Ci.nsITimer.TYPE_ONE_SHOT);
+
     this.proxy.connect(function() {
-      this.saveRequestAndResponseBodies = this._saveRequestAndResponseBodies;
-      this._onInitComplete();
+      if (this._connectTimer) {
+        this._connectTimer.cancel();
+        this._connectTimer = null;
+        this.saveRequestAndResponseBodies = this._saveRequestAndResponseBodies;
+        this._onInitComplete();
+      }
     }.bind(this));
   },
 
   /**
+   * Connection timeout handler. This method simply prints a message informing
+   * the user that the connection timed-out.
+   * @private
+   */
+  _connectionTimeout: function WCF__connectionTimeout()
+  {
+    this._connectTimer = null;
+
+    let node = this.createMessageNode(CATEGORY_JS, SEVERITY_ERROR,
+                                      l10n.getStr("connectionTimeout"));
+    this.outputMessage(CATEGORY_JS, node);
+
+    // Allow initialization to complete.
+    this._onInitComplete();
+  },
+
+  /**
+   * Reset the connection timeout timer.
+   * @private
+   */
+  _resetConnectionTimeout: function WCF__resetConnectionTimeout()
+  {
+    let timer = this._connectTimer;
+    if (timer) {
+      let timeout = timer.delay;
+      timer.cancel();
+      timer.initWithCallback(this._connectionTimeout, timeout,
+                             Ci.nsITimer.TYPE_ONE_SHOT);
+    }
+  },
+
+  /**
    * Find the Web Console UI elements and setup event listeners as needed.
    * @private
    */
   _initUI: function WCF__initUI()
   {
     let doc = this.document;
 
     this.filterBox = doc.querySelector(".hud-filter-box");
@@ -2727,16 +2778,21 @@ WebConsoleFrame.prototype = {
     this._networkRequests = {};
 
     if (this._outputTimerInitialized) {
       this._outputTimerInitialized = false;
       this._outputTimer.cancel();
     }
     this._outputTimer = null;
 
+    if (this._connectTimer) {
+      this._connectTimer.cancel();
+    }
+    this._connectTimer = null;
+
     if (this.proxy) {
       this.proxy.disconnect(aOnDestroy);
       this.proxy = null;
     }
 
     if (this.jsterm) {
       this.jsterm.destroy();
       this.jsterm = null;
@@ -4087,16 +4143,18 @@ WebConsoleConnectionProxy.prototype = {
    * @param object aResponse
    *        The JSON response object received from the server.
    */
   _onListTabs: function WCCP__onListTabs(aCallback, aResponse)
   {
     let selectedTab;
 
     if (this.remoteHost) {
+      this.owner._connectTimer.cancel();
+
       let tabs = [];
       for (let tab of aResponse.tabs) {
         tabs.push(tab.title);
       }
 
       tabs.push(l10n.getStr("listTabs.globalConsoleActor"));
 
       let selected = {};
@@ -4116,16 +4174,18 @@ WebConsoleConnectionProxy.prototype = {
     if (selectedTab) {
       this._consoleActor = selectedTab.consoleActor;
       this.owner.onLocationChange(selectedTab.url, selectedTab.title);
     }
     else {
       this._consoleActor = aResponse.consoleActor;
     }
 
+    this.owner._resetConnectionTimeout();
+
     let listeners = ["PageError", "ConsoleAPI", "NetworkActivity",
                      "FileActivity", "LocationChange"];
     this.client.attachConsole(this._consoleActor, listeners,
                               this._onAttachConsole.bind(this, aCallback));
   },
 
   /**
    * The "attachConsole" response handler.
@@ -4313,23 +4373,48 @@ WebConsoleConnectionProxy.prototype = {
    */
   disconnect: function WCCP_disconnect(aOnDisconnect)
   {
     if (!this.client) {
       aOnDisconnect && aOnDisconnect();
       return;
     }
 
+    let onDisconnect = function() {
+      if (timer) {
+        timer.cancel();
+        timer = null;
+      }
+      if (aOnDisconnect) {
+        aOnDisconnect();
+        aOnDisconnect = null;
+      }
+    };
+
+    let timer = null;
+    if (aOnDisconnect) {
+      timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+      timer.initWithCallback(onDisconnect, 1500, Ci.nsITimer.TYPE_ONE_SHOT);
+    }
+
     this.client.removeListener("pageError", this._onPageError);
     this.client.removeListener("consoleAPICall", this._onConsoleAPICall);
     this.client.removeListener("networkEvent", this._onNetworkEvent);
     this.client.removeListener("networkEventUpdate", this._onNetworkEventUpdate);
     this.client.removeListener("fileActivity", this._onFileActivity);
     this.client.removeListener("locationChange", this._onLocationChange);
-    this.client.close(aOnDisconnect);
+
+    try {
+      this.client.close(onDisconnect);
+    }
+    catch (ex) {
+      Cu.reportError("Web Console disconnect exception: " + ex);
+      Cu.reportError(ex.stack);
+      onDisconnect();
+    }
 
     this.client = null;
     this.webConsoleClient = null;
     this.connected = false;
     this.owner = null;
   },
 };
 
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -49,16 +49,17 @@
 #endif
 
 [xpcom]
 @BINPATH@/dependentlibs.list
 #ifdef XP_WIN32
 @BINPATH@/@DLL_PREFIX@gkmedias@DLL_SUFFIX@
 #endif
 @BINPATH@/@DLL_PREFIX@mozalloc@DLL_SUFFIX@
+@BINPATH@/@DLL_PREFIX@soundtouch@DLL_SUFFIX@
 #ifdef MOZ_SHARED_MOZGLUE
 @BINPATH@/@DLL_PREFIX@mozglue@DLL_SUFFIX@
 #endif
 #ifndef MOZ_STATIC_JS
 @BINPATH@/@DLL_PREFIX@mozjs@DLL_SUFFIX@
 #endif
 #ifndef MOZ_NATIVE_NSPR
 @BINPATH@/@DLL_PREFIX@nspr4@DLL_SUFFIX@
--- a/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
@@ -179,8 +179,12 @@ executeEmptyInput=No value to execute.
 # displayed in the network panel when the response body is only partially
 # available.
 NetworkPanel.fetchRemainingResponseContentLink=Fetch the remaining %1$S bytes
 
 # LOCALIZATION NOTE (NetworkPanel.fetchRemainingRequestContentLink): This is
 # displayed in the network panel when the request body is only partially
 # available.
 NetworkPanel.fetchRemainingRequestContentLink=Fetch the request body (%1$S bytes)
+
+# LOCALIZATION NOTE (connectionTimeout): Message displayed when the Remote Web
+# Console fails to connect to the server due to a timeout.
+connectionTimeout=Connection timeout. Check the Error Console on both ends for potential error messages. Reopen the Web Console to try again.
--- a/browser/locales/en-US/chrome/browser/taskbar.properties
+++ b/browser/locales/en-US/chrome/browser/taskbar.properties
@@ -5,10 +5,12 @@
 taskbar.tasks.newTab.label=Open new tab
 taskbar.tasks.newTab.description=Open a new browser tab.
 taskbar.tasks.newWindow.label=Open new window
 taskbar.tasks.newWindow.description=Open a new browser window.
 taskbar.tasks.enterPrivacyMode.label=Enter private browsing
 taskbar.tasks.enterPrivacyMode.description=Start private browsing. The current session will be saved.
 taskbar.tasks.exitPrivacyMode.label=Quit private browsing
 taskbar.tasks.exitPrivacyMode.description=Quit private browsing and restore the previous session.
+taskbar.tasks.newPrivateWindow.label=New private window
+taskbar.tasks.newPrivateWindow.description=Open a new window in private browsing mode.
 taskbar.frequent.label=Frequent
 taskbar.recent.label=Recent
--- a/browser/modules/Makefile.in
+++ b/browser/modules/Makefile.in
@@ -26,13 +26,15 @@ EXTRA_JS_MODULES = \
 	webappsUI.jsm \
 	webrtcUI.jsm \
 	KeywordURLResetPrompter.jsm \
 	$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 EXTRA_JS_MODULES += \
 	WindowsPreviewPerTab.jsm \
+	$(NULL)
+EXTRA_PP_JS_MODULES = \
 	WindowsJumpLists.jsm \
 	$(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/modules/WindowsJumpLists.jsm
+++ b/browser/modules/WindowsJumpLists.jsm
@@ -69,19 +69,21 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this, "_taskbarService",
                                    "@mozilla.org/windows-taskbar;1",
                                    "nsIWinTaskbar");
 
 XPCOMUtils.defineLazyServiceGetter(this, "_winShellService",
                                    "@mozilla.org/browser/shell-service;1",
                                    "nsIWindowsShellService");
 
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
 XPCOMUtils.defineLazyServiceGetter(this, "_privateBrowsingSvc",
                                    "@mozilla.org/privatebrowsing;1",
                                    "nsIPrivateBrowsingService");
+#endif
 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 /**
  * Global functions
  */
 
@@ -99,39 +101,51 @@ var tasksCfg = [
    * title       - Task title displayed in the list. (strings in the table are temp fillers.)
    * description - Tooltip description on the list item.
    * args        - Command line args to invoke the task.
    * iconIndex   - Optional win icon index into the main application for the
    *               list item.
    * open        - Boolean indicates if the command should be visible after the browser opens.
    * close       - Boolean indicates if the command should be visible after the browser closes.
    */
-  // Open new window
+  // Open new tab
   {
     get title()       _getString("taskbar.tasks.newTab.label"),
     get description() _getString("taskbar.tasks.newTab.description"),
     args:             "-new-tab about:blank",
     iconIndex:        3, // New window icon
     open:             true,
     close:            true, // The jump list already has an app launch icon, but
                             // we don't always update the list on shutdown.
                             // Thus true for consistency.
   },
 
-  // Open new tab
+  // Open new window
   {
     get title()       _getString("taskbar.tasks.newWindow.label"),
     get description() _getString("taskbar.tasks.newWindow.description"),
     args:             "-browser",
     iconIndex:        2, // New tab icon
     open:             true,
     close:            true, // No point, but we don't always update the list on
-                            //  shutdown.  Thus true for consistency.
+                            // shutdown. Thus true for consistency.
   },
 
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+  // Open new private window
+  {
+    get title()       _getString("taskbar.tasks.newPrivateWindow.label"),
+    get description() _getString("taskbar.tasks.newPrivateWindow.description"),
+    args:             "-private-window",
+    iconIndex:        4, // Private browsing mode icon
+    open:             true,
+    close:            true, // No point, but we don't always update the list on
+                            // shutdown. Thus true for consistency.
+  },
+#else
   // Toggle the Private Browsing mode
   {
     get title() {
       if (_privateBrowsingSvc.privateBrowsingEnabled)
         return _getString("taskbar.tasks.exitPrivacyMode.label");
       else
         return _getString("taskbar.tasks.enterPrivacyMode.label");
     },
@@ -147,16 +161,17 @@ var tasksCfg = [
       // Don't show when inside permanent private browsing mode
       return !PrivateBrowsingUtils.permanentPrivateBrowsing;
     },
     get close() {
       // Don't show when inside permanent private browsing mode
       return !PrivateBrowsingUtils.permanentPrivateBrowsing;
     },
   },
+#endif
 ];
 
 /////////////////////////////////////////////////////////////////////////////
 // Implementation
 
 this.WinTaskbarJumpList =
 {
   _builder: null,
@@ -509,27 +524,31 @@ this.WinTaskbarJumpList =
     this._builder = _taskbarService.createJumpListBuilder();
     if (!this._builder || !this._builder.available)
       return false;
 
     return true;
   },
 
   _initObs: function WTBJL__initObs() {
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
     Services.obs.addObserver(this, "private-browsing", false);
+#endif
     // If the browser is closed while in private browsing mode, the "exit"
     // notification is fired on quit-application-granted.
     // History cleanup can happen at profile-change-teardown.
     Services.obs.addObserver(this, "profile-before-change", false);
     Services.obs.addObserver(this, "browser:purge-session-history", false);
     _prefs.addObserver("", this, false);
   },
  
   _freeObs: function WTBJL__freeObs() {
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
     Services.obs.removeObserver(this, "private-browsing");
+#endif
     Services.obs.removeObserver(this, "profile-before-change");
     Services.obs.removeObserver(this, "browser:purge-session-history");
     _prefs.removeObserver("", this);
   },
 
   _updateTimer: function WTBJL__updateTimer() {
     if (this._enabled && !this._shuttingDown && !this._timer) {
       this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
@@ -585,21 +604,21 @@ this.WinTaskbarJumpList =
 
       case "profile-before-change":
         this._shutdown();
       break;
 
       case "browser:purge-session-history":
         this.update();
       break;
-
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
       case "private-browsing":
         this.update();
       break;
-
+#endif
       case "idle":
         if (this._timer) {
           this._timer.cancel();
           delete this._timer;
         }
       break;
 
       case "back":
--- a/browser/themes/gnomestripe/downloads/downloads.css
+++ b/browser/themes/gnomestripe/downloads/downloads.css
@@ -20,17 +20,17 @@
 
 #downloadsHistory {
   background: transparent;
   color: -moz-nativehyperlinktext;
   cursor: pointer;
 }
 
 #downloadsSummary,
-#downloadsPanel[hasdownloads] > #downloadsHistory {
+#downloadsPanel[hasdownloads] #downloadsHistory {
   border-top: 1px solid ThreeDShadow;
   background-image: -moz-linear-gradient(hsla(0,0%,0%,.15), hsla(0,0%,0%,.08) 6px);
 }
 
 #downloadsHistory > .button-box {
   margin: 1em;
 }
 
@@ -45,16 +45,22 @@ richlistitem[type="download"] {
   height: 6em;
   -moz-padding-end: 0;
   color: inherit;
 }
 
 #downloadsSummary {
   padding: 8px 38px 8px 12px;
   cursor: pointer;
+  -moz-user-focus: normal;
+}
+
+#downloadsSummary:-moz-focusring {
+  outline: 1px -moz-dialogtext dotted;
+  outline-offset: -5px;
 }
 
 #downloadsSummary > .downloadTypeIcon {
   height: 32px;
   width: 32px;
   list-style-image: url("chrome://mozapps/skin/downloads/downloadIcon.png");
 }
 
--- a/browser/themes/pinstripe/downloads/downloads.css
+++ b/browser/themes/pinstripe/downloads/downloads.css
@@ -21,56 +21,62 @@
 #downloadsHistory {
   background: transparent;
   border-bottom-left-radius: 6px;
   border-bottom-right-radius: 6px;
   color: hsl(210,100%,75%);
   cursor: pointer;
 }
 
-#downloadsPanel:not([hasdownloads]) > #downloadsHistory {
+#downloadsPanel:not([hasdownloads]) #downloadsHistory {
   border-top-left-radius: 6px;
   border-top-right-radius: 6px;
 }
 
 #downloadsSummary,
-#downloadsPanel[hasdownloads] > #downloadsHistory {
+#downloadsPanel[hasdownloads] #downloadsHistory {
   background: #e5e5e5;
   border-top: 1px solid hsla(0,0%,0%,.1);
   box-shadow: 0 -1px hsla(0,0%,100%,.5) inset, 0 1px 1px hsla(0,0%,0%,.03) inset;
 }
 
 #downloadsHistory > .button-box {
   color: #808080;
   margin: 1em;
 }
 
 #downloadsHistory:-moz-focusring > .button-box {
   outline: 1px -moz-dialogtext dotted;
   border-top-left-radius: 6px;
   border-top-right-radius: 6px;
 }
 
-#downloadsPanel:not([hasdownloads]) > #downloadsHistory:-moz-focusring > .button-box {
+#downloadsPanel:not([hasdownloads]) #downloadsHistory:-moz-focusring > .button-box {
   border-bottom-left-radius: 6px;
   border-bottom-right-radius: 6px;
 }
 
 /*** Downloads Summary and List items ***/
 
 #downloadsSummary,
 richlistitem[type="download"] {
   height: 7em;
   -moz-padding-end: 0;
   color: inherit;
 }
 
 #downloadsSummary {
   padding: 8px 38px 8px 12px;
   cursor: pointer;
+  -moz-user-focus: normal;
+}
+
+#downloadsSummary:-moz-focusring {
+  outline: 1px -moz-dialogtext dotted;
+  outline-offset: -5px;
 }
 
 #downloadsSummary > .downloadTypeIcon {
   list-style-image: url("chrome://mozapps/skin/downloads/downloadIcon.png");
 }
 
 #downloadsSummaryDescription {
   color: -moz-nativehyperlinktext;
--- a/browser/themes/winstripe/downloads/downloads-aero.css
+++ b/browser/themes/winstripe/downloads/downloads-aero.css
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 %define WINSTRIPE_AERO
 %include downloads.css
 %undef WINSTRIPE_AERO
 
 @media (-moz-windows-default-theme) {
-  #downloadsPanel[hasdownloads] > #downloadsHistory {
+  #downloadsPanel[hasdownloads] #downloadsHistory {
     background-color: #f1f5fb;
   }
 
   richlistitem[type="download"] {
     border: 1px solid transparent;
     border-bottom: 1px solid hsl(213,40%,90%);
   }
 
--- a/browser/themes/winstripe/downloads/downloads.css
+++ b/browser/themes/winstripe/downloads/downloads.css
@@ -25,17 +25,17 @@
 }
 
 #downloadsHistory > .button-box {
   margin: 1em;
 }
 
 @media (-moz-windows-default-theme) {
   #downloadsSummary,
-  #downloadsPanel[hasdownloads] > #downloadsHistory {
+  #downloadsPanel[hasdownloads] #downloadsHistory {
     background-color: hsla(216,45%,88%,.98);
     box-shadow: 0px 1px 2px rgb(204,214,234) inset;
   }
 }
 
 /*** Downloads Summary and List items ***/
 
 #downloadsSummary,
@@ -43,16 +43,22 @@ richlistitem[type="download"] {
   height: 7em;
   -moz-padding-end: 0;
   color: inherit;
 }
 
 #downloadsSummary {
   padding: 8px 38px 8px 12px;
   cursor: pointer;
+  -moz-user-focus: normal;
+}
+
+#downloadsSummary:-moz-focusring {
+  outline: 1px -moz-dialogtext dotted;
+  outline-offset: -5px;
 }
 
 #downloadsSummary > .downloadTypeIcon {
   height: 24px;
   width: 24px;
   list-style-image: url("chrome://mozapps/skin/downloads/downloadIcon.png");
 }
 
--- a/config/system-headers
+++ b/config/system-headers
@@ -977,16 +977,17 @@ plbase64.h
 plerror.h
 plgetopt.h
 plresolv.h
 plstr.h
 plarenas.h
 plarena.h
 plhash.h
 speex/speex_resampler.h
+soundtouch/SoundTouch.h
 #if MOZ_NATIVE_PNG==1
 png.h
 #endif
 #if MOZ_NATIVE_ZLIB==1
 zlib.h
 #endif
 #ifdef MOZ_ENABLE_STARTUP_NOTIFICATION
 libsn/sn.h
--- a/configure.in
+++ b/configure.in
@@ -4188,16 +4188,17 @@ MOZ_FEEDS=1
 MOZ_FLEXBOX=1
 MOZ_WEBAPP_RUNTIME=
 MOZ_JSDEBUGGER=1
 MOZ_AUTH_EXTENSION=1
 MOZ_OGG=1
 MOZ_RAW=
 MOZ_SYDNEYAUDIO=
 MOZ_SPEEX_RESAMPLER=1
+MOZ_SOUNDTOUCH=1
 MOZ_CUBEB=
 MOZ_VORBIS=
 MOZ_TREMOR=
 MOZ_WAVE=1
 MOZ_SAMPLE_TYPE_FLOAT32=
 MOZ_SAMPLE_TYPE_S16=
 MOZ_MEDIA=
 MOZ_OPUS=1
@@ -5547,16 +5548,29 @@ dnl ====================================
 if test -n "$MOZ_SYDNEYAUDIO"; then
     AC_DEFINE(MOZ_SYDNEYAUDIO)
 fi
 
 if test -n "$MOZ_SPEEX_RESAMPLER"; then
     AC_DEFINE(MOZ_SPEEX_RESAMPLER)
 fi
 
+if test -n "$MOZ_SOUNDTOUCH"; then
+    AC_DEFINE(MOZ_SOUNDTOUCH)
+fi
+
+if test -z "$GNU_CC" -a "$OS_ARCH" = "WINNT"; then
+   SOUNDTOUCH_LIBS='$(LIBXUL_DIST)/lib/$(LIB_PREFIX)soundtouch.$(LIB_SUFFIX)'
+else
+    SOUNDTOUCH_LIBS='-lsoundtouch'
+fi
+AC_SUBST(SOUNDTOUCH_CFLAGS)
+AC_SUBST(SOUNDTOUCH_LIBS)
+AC_SUBST(SOUNDTOUCH_CONFIG)
+
 if test -n "$MOZ_CUBEB"; then
     case "$target" in
     *-android*|*-linuxandroid*)
         if test -n "$gonkdir"; then
             AC_DEFINE(MOZ_CUBEB)
         fi
         dnl No Android implementation of libcubeb yet.
         ;;
@@ -8600,16 +8614,17 @@ AC_SUBST(MSMANIFEST_TOOL)
 AC_SUBST(NS_ENABLE_TSF)
 AC_SUBST(MOZ_NSS_PATCH)
 AC_SUBST(MOZ_APP_COMPONENT_LIBS)
 AC_SUBST(MOZ_APP_EXTRA_LIBS)
 
 AC_SUBST(MOZ_MEDIA)
 AC_SUBST(MOZ_SYDNEYAUDIO)
 AC_SUBST(MOZ_SPEEX_RESAMPLER)
+AC_SUBST(MOZ_SOUNDTOUCH)
 AC_SUBST(MOZ_CUBEB)
 AC_SUBST(MOZ_WAVE)
 AC_SUBST(MOZ_VORBIS)
 AC_SUBST(MOZ_TREMOR)
 AC_SUBST(MOZ_OPUS)
 AC_SUBST(MOZ_WEBM)
 AC_SUBST(MOZ_DASH)
 AC_SUBST(MOZ_MEDIA_PLUGINS)
--- a/content/base/public/DirectionalityUtils.h
+++ b/content/base/public/DirectionalityUtils.h
@@ -2,34 +2,39 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DirectionalityUtils_h___
 #define DirectionalityUtils_h___
 
+#include "prtypes.h"
+#include "mozilla/StandardInteger.h"
+
 class nsIContent;
 class nsIDocument;
 class nsINode;
+class nsAString;
+class nsAttrValue;
+class nsTextNode;
 
 namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 namespace mozilla {
 
-namespace directionality {
-
 enum Directionality {
-  eDir_NotSet = 0,
-  eDir_RTL    = 1,
-  eDir_LTR    = 2
+  eDir_NotSet,
+  eDir_RTL,
+  eDir_LTR,
+  eDir_Auto
 };
 
 /**
  * Set the directionality of an element according to the algorithm defined at
  * http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-directionality,
  * not including elements with auto direction.
  *
  * @return the directionality that the element was set to
@@ -39,17 +44,90 @@ Directionality RecomputeDirectionality(m
 
 /**
  * Set the directionality of any descendants of a node that do not themselves
  * have a dir attribute.
  * For performance reasons we walk down the descendant tree in the rare case
  * of setting the dir attribute, rather than walking up the ancestor tree in
  * the much more common case of getting the element's directionality.
  */
-void SetDirectionalityOnDescendants(mozilla::dom::Element* aElement, 
+void SetDirectionalityOnDescendants(mozilla::dom::Element* aElement,
                                     Directionality aDir,
                                     bool aNotify = true);
 
-} // end namespace directionality
+/**
+ * Walk the descendants of a node in tree order and, for any text node
+ * descendant that determines the directionality of some element and is not a
+ * descendant of another descendant of the original node with dir=auto,
+ * redetermine that element's directionality
+  */
+void WalkDescendantsResetAutoDirection(mozilla::dom::Element* aElement);
+
+/**
+ * After setting dir=auto on an element, walk its descendants in tree order.
+ * If the node doesn't have the NODE_ANCESTOR_HAS_DIR_AUTO flag, set the
+ * NODE_ANCESTOR_HAS_DIR_AUTO flag on all of its descendants.
+ * Resolve the directionality of the element by the "downward propagation
+ * algorithm" (defined in section 3 in the comments at the beginning of
+ * DirectionalityUtils.cpp)
+ */
+void WalkDescendantsSetDirAuto(mozilla::dom::Element* aElement,
+                               bool aNotify = true);
+
+/**
+ * After unsetting dir=auto on an element, walk its descendants in tree order,
+ * skipping any that have dir=auto themselves, and unset the
+ * NODE_ANCESTOR_HAS_DIR_AUTO flag
+ */
+void WalkDescendantsClearAncestorDirAuto(mozilla::dom::Element* aElement);
+
+/**
+ * Walk the parent chain of a text node whose dir attribute has been removed and
+ * reset the direction of any of its ancestors which have dir=auto and whose
+ * directionality is determined by a text node descendant.
+ */
+void WalkAncestorsResetAutoDirection(mozilla::dom::Element* aElement,
+                                     bool aNotify = true);
+
+/**
+ * When the contents of a text node have changed, deal with any elements whose
+ * directionality needs to change
+ */
+void SetDirectionFromChangedTextNode(nsIContent* aTextNode, uint32_t aOffset,
+                                     const PRUnichar* aBuffer, uint32_t aLength,
+                                     bool aNotify);
+
+/**
+ * When a text node is appended to an element, find any ancestors with dir=auto
+ * whose directionality will be determined by the text node
+ */
+void SetDirectionFromNewTextNode(nsTextNode* aTextNode);
+
+/**
+ * When a text node is removed from a document, find any ancestors whose
+ * directionality it determined and redetermine their directionality
+ */
+void ResetDirectionSetByTextNode(nsTextNode* aTextNode);
+
+/**
+ * Set the directionality of an element according to the directionality of the
+ * text in aValue
+ */
+void SetDirectionalityFromValue(mozilla::dom::Element* aElement,
+                                const nsAString& aValue,
+                                bool aNotify);
+
+/**
+ * Called when setting the dir attribute on an element, immediately after
+ * AfterSetAttr. This is instead of using BeforeSetAttr or AfterSetAttr, because
+ * in AfterSetAttr we don't know the old value, so we can't identify all cases
+ * where we need to walk up or down the document tree and reset the direction;
+ * and in BeforeSetAttr we can't do the walk because this element hasn't had the
+ * value set yet so the results will be wrong.
+ */
+void OnSetDirAttr(mozilla::dom::Element* aElement,
+                  const nsAttrValue* aNewValue,
+                  bool hadValidDir,
+                  bool aNotify);
 
 } // end namespace mozilla
 
 #endif /* DirectionalityUtils_h___ */
--- a/content/base/public/Element.h
+++ b/content/base/public/Element.h
@@ -277,44 +277,43 @@ public:
 
   /**
    * Returns an atom holding the name of the "class" attribute on this
    * content node (if applicable).  Returns null if there is no
    * "class" attribute for this type of content node.
    */
   virtual nsIAtom *GetClassAttributeName() const;
 
-  inline directionality::Directionality GetDirectionality() const {
+  inline Directionality GetDirectionality() const {
     if (HasFlag(NODE_HAS_DIRECTION_RTL)) {
-      return directionality::eDir_RTL;
+      return eDir_RTL;
     }
 
     if (HasFlag(NODE_HAS_DIRECTION_LTR)) {
-      return directionality::eDir_LTR;
+      return eDir_LTR;
     }
 
-    return directionality::eDir_NotSet;
+    return eDir_NotSet;
   }
 
-  inline void SetDirectionality(directionality::Directionality aDir,
-                                bool aNotify) {
+  inline void SetDirectionality(Directionality aDir, bool aNotify) {
     UnsetFlags(NODE_ALL_DIRECTION_FLAGS);
     if (!aNotify) {
       RemoveStatesSilently(DIRECTION_STATES);
     }
 
     switch (aDir) {
-      case (directionality::eDir_RTL):
+      case (eDir_RTL):
         SetFlags(NODE_HAS_DIRECTION_RTL);
         if (!aNotify) {
           AddStatesSilently(NS_EVENT_STATE_RTL);
         }
         break;
 
-      case(directionality::eDir_LTR):
+      case(eDir_LTR):
         SetFlags(NODE_HAS_DIRECTION_LTR);
         if (!aNotify) {
           AddStatesSilently(NS_EVENT_STATE_LTR);
         }
         break;
 
       default:
         break;
@@ -327,16 +326,25 @@ public:
      */
     if (aNotify) {
       UpdateState(true);
     }
   }
 
   bool GetBindingURL(nsIDocument *aDocument, css::URLValue **aResult);
 
+  // The bdi element defaults to dir=auto if it has no dir attribute set.
+  // Other elements will only have dir=auto if they have an explicit dir=auto,
+  // which will mean that HasValidDir() returns true but HasFixedDir() returns
+  // false
+  inline bool HasDirAuto() const {
+    return (!HasFixedDir() &&
+            (HasValidDir() || NodeInfo()->Equals(nsGkAtoms::bdi)));
+  }
+
 protected:
   /**
    * Method to get the _intrinsic_ content state of this element.  This is the
    * state that is independent of the element's presentation.  To get the full
    * content state, use State().  See nsEventStates.h for
    * the possible bits that could be set here.
    */
   virtual nsEventStates IntrinsicState() const;
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -414,17 +414,17 @@ public:
    * Set the sandbox flags for this document.
    * @see nsSandboxFlags.h for the possible flags
    */
   void SetSandboxFlags(uint32_t sandboxFlags)
   {
     mSandboxFlags = sandboxFlags;
   }
 
-  inline mozilla::directionality::Directionality GetDocumentDirectionality() {
+  inline mozilla::Directionality GetDocumentDirectionality() {
     return mDirectionality;
   }
   
   /**
    * Access HTTP header data (this may also get set from other
    * sources, like HTML META tags).
    */
   virtual void GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const = 0;
@@ -1911,17 +1911,17 @@ protected:
   uint32_t mBidiOptions;
 
   // The sandbox flags on the document. These reflect the value of the sandbox attribute of the
   // associated IFRAME or CSP-protectable content, if existent. These are set at load time and
   // are immutable - see nsSandboxFlags.h for the possible flags.
   uint32_t mSandboxFlags;
 
   // The root directionality of this document.
-  mozilla::directionality::Directionality mDirectionality;
+  mozilla::Directionality mDirectionality;
 
   nsCString mContentLanguage;
 private:
   nsCString mContentType;
 protected:
 
   // The document's security info
   nsCOMPtr<nsISupports> mSecurityInfo;
@@ -2055,20 +2055,20 @@ public:
   ~nsAutoSyncOperation();
 private:
   nsCOMArray<nsIDocument> mDocuments;
   uint32_t                mMicroTaskLevel;
 };
 
 // XXX These belong somewhere else
 nsresult
-NS_NewHTMLDocument(nsIDocument** aInstancePtrResult);
+NS_NewHTMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData = false);
 
 nsresult
-NS_NewXMLDocument(nsIDocument** aInstancePtrResult);
+NS_NewXMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData = false);
 
 nsresult
 NS_NewSVGDocument(nsIDocument** aInstancePtrResult);
 
 nsresult
 NS_NewImageDocument(nsIDocument** aInstancePtrResult);
 
 #ifdef MOZ_MEDIA
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -1290,18 +1290,31 @@ private:
     // Set if element has pointer locked
     ElementHasPointerLock,
     // Set if the node may have DOMMutationObserver attached to it.
     NodeMayHaveDOMMutationObserver,
     // Set if node is Content
     NodeIsContent,
     // Set if the node has animations or transitions
     ElementHasAnimations,
-    // Set if node has a dir attribute with a valid value (ltr or rtl)
+    // Set if node has a dir attribute with a valid value (ltr, rtl, or auto)
     NodeHasValidDirAttribute,
+    // Set if node has a dir attribute with a fixed value (ltr or rtl, NOT auto)
+    NodeHasFixedDir,
+    // Set if the node has dir=auto and has a property pointing to the text
+    // node that determines its direction
+    NodeHasDirAutoSet,
+    // Set if the node is a text node descendant of a node with dir=auto
+    // and has a TextNodeDirectionalityMap property listing the elements whose
+    // direction it determines.
+    NodeHasTextNodeDirectionalityMap,
+    // Set if the node has dir=auto.
+    NodeHasDirAuto,
+    // Set if a node in the node's parent chain has dir=auto.
+    NodeAncestorHasDirAuto,
     // Guard value
     BooleanFlagCount
   };
 
   void SetBoolFlag(BooleanFlag name, bool value) {
     PR_STATIC_ASSERT(BooleanFlagCount <= 8*sizeof(mBoolFlags));
     mBoolFlags = (mBoolFlags & ~(1 << name)) | (value << name);
   }
@@ -1362,16 +1375,62 @@ public:
   bool HasPointerLock() const { return GetBoolFlag(ElementHasPointerLock); }
   void SetPointerLock() { SetBoolFlag(ElementHasPointerLock); }
   void ClearPointerLock() { ClearBoolFlag(ElementHasPointerLock); }
   bool MayHaveAnimations() { return GetBoolFlag(ElementHasAnimations); }
   void SetMayHaveAnimations() { SetBoolFlag(ElementHasAnimations); }
   void SetHasValidDir() { SetBoolFlag(NodeHasValidDirAttribute); }
   void ClearHasValidDir() { ClearBoolFlag(NodeHasValidDirAttribute); }
   bool HasValidDir() const { return GetBoolFlag(NodeHasValidDirAttribute); }
+  void SetHasFixedDir() {
+    MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE,
+               "SetHasFixedDir on text node");
+    SetBoolFlag(NodeHasFixedDir);
+  }
+  void ClearHasFixedDir() {
+    MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE,
+               "ClearHasFixedDir on text node");
+    ClearBoolFlag(NodeHasFixedDir);
+  }
+  bool HasFixedDir() const { return GetBoolFlag(NodeHasFixedDir); }
+  void SetHasDirAutoSet() {
+    MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE,
+               "SetHasDirAutoSet on text node");
+    SetBoolFlag(NodeHasDirAutoSet);
+  }
+  void ClearHasDirAutoSet() {
+    MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE,
+               "ClearHasDirAutoSet on text node");
+    ClearBoolFlag(NodeHasDirAutoSet);
+  }
+  bool HasDirAutoSet() const
+    { return GetBoolFlag(NodeHasDirAutoSet); }
+  void SetHasTextNodeDirectionalityMap() {
+    MOZ_ASSERT(NodeType() == nsIDOMNode::TEXT_NODE,
+               "SetHasTextNodeDirectionalityMap on non-text node");
+    SetBoolFlag(NodeHasTextNodeDirectionalityMap);
+  }
+  void ClearHasTextNodeDirectionalityMap() {
+    MOZ_ASSERT(NodeType() == nsIDOMNode::TEXT_NODE,
+               "ClearHasTextNodeDirectionalityMap on non-text node");
+    ClearBoolFlag(NodeHasTextNodeDirectionalityMap);
+  }
+  bool HasTextNodeDirectionalityMap() const
+    { return GetBoolFlag(NodeHasTextNodeDirectionalityMap); }
+
+  void SetHasDirAuto() { SetBoolFlag(NodeHasDirAuto); }
+  void ClearHasDirAuto() { ClearBoolFlag(NodeHasDirAuto); }
+  bool HasDirAuto() const { return GetBoolFlag(NodeHasDirAuto); }
+
+  void SetAncestorHasDirAuto() { SetBoolFlag(NodeAncestorHasDirAuto); }
+  void ClearAncestorHasDirAuto() { ClearBoolFlag(NodeAncestorHasDirAuto); }
+  bool AncestorHasDirAuto() const { return GetBoolFlag(NodeAncestorHasDirAuto); }
+
+  bool NodeOrAncestorHasDirAuto() const
+    { return HasDirAuto() || AncestorHasDirAuto(); }
 protected:
   void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
   void SetInDocument() { SetBoolFlag(IsInDocument); }
   void SetNodeIsContent() { SetBoolFlag(NodeIsContent); }
   void ClearInDocument() { ClearBoolFlag(IsInDocument); }
   void SetIsElement() { SetBoolFlag(NodeIsElement); }
   void SetHasID() { SetBoolFlag(ElementHasID); }
   void ClearHasID() { ClearBoolFlag(ElementHasID); }
--- a/content/base/src/DirectionalityUtils.cpp
+++ b/content/base/src/DirectionalityUtils.cpp
@@ -1,33 +1,562 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et tw=78: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/*
+  Implementation description from https://etherpad.mozilla.org/dir-auto
+
+  Static case
+  ===========
+  When we see a new content node with @dir=auto from the parser, we set the
+  NodeHasDirAuto flag on the node.  We won't have enough information to
+  decide the directionality of the node at this point.
+
+  When we bind a new content node to the document, if its parent has either of
+  the NodeAncestorHasDirAuto or NodeHasDirAuto flags, we set the
+  NodeAncestorHasDirAuto flag on the node.
+
+  When a new input with @type=text/search/tel/url/email and @dir=auto is added
+  from the parser, we resolve the directionality based on its @value.
+
+  When a new text node with non-neutral content is appended to a textarea
+  element with NodeHasDirAuto, if the directionality of the textarea element
+  is still unresolved, it is resolved based on the value of the text node.
+  Elements with unresolved directionality behave as LTR.
+
+  When a new text node with non-neutral content is appended to an element that
+  is not a textarea but has either of the NodeAncestorHasDirAuto or
+  NodeHasDirAuto flags, we walk up the parent chain while the
+  NodeAncestorHasDirAuto flag is present, and when we reach an element with
+  NodeHasDirAuto and no resolved directionality, we resolve the directionality
+  based on the contents of the text node and cease walking the parent chain.
+  Note that we should ignore elements with NodeHasDirAuto with resolved
+  directionality, so that the second text node in this example tree doesn't
+  affect the directionality of the div:
+
+  <div dir=auto>
+    <span>foo</span>
+    <span>بار</span>
+  </div>
+
+  The parent chain walk will be aborted if we hit a script or style element, or
+  if we hit an element with @dir=ltr or @dir=rtl.
+
+  I will call this algorithm "upward propagation".
+
+  Each text node should maintain a list of elements which have their
+  directionality determined by the first strong character of that text node.
+  This is useful to make dynamic changes more efficient.  One way to implement
+  this is to have a per-document hash table mapping a text node to a set of
+  elements.  I'll call this data structure TextNodeDirectionalityMap. The
+  algorithm for appending a new text node above needs to update this data
+  structure.
+
+  *IMPLEMENTATION NOTE*
+  In practice, the implementation uses two per-node properties:
+
+  dirAutoSetBy, which is set on a node with auto-directionality, and points to
+  the textnode that contains the strong character which determines the
+  directionality of the node.
+
+  textNodeDirectionalityMap, which is set on a text node and points to a hash
+  table listing the nodes whose directionality is determined by the text node.
+
+  Handling dynamic changes
+  ========================
+
+  We need to handle the following cases:
+
+  1. When the value of an input element with @type=text/search/tel/url/email is
+  changed, if it has NodeHasDirAuto, we update the resolved directionality.
+
+  2. When the dir attribute is changed from something else (including the case
+  where it doesn't exist) to auto on a textarea or an input element with
+  @type=text/search/tel/url/email, we set the NodeHasDirAuto flag and resolve
+  the directionality based on the value of the element.
+
+  3. When the dir attribute is changed from something else (including the case
+  where it doesn't exist) to auto on any element except case 1 above and the bdi
+  element, we run the following algorithm:
+  * We set the NodeHasDirAuto flag.
+  * If the element doesn't have the NodeAncestorHasDirAuto flag, we set the
+  NodeAncestorHasDirAuto flag on all of its child nodes.  (Note that if the
+  element does have NodeAncestorHasDirAuto, all of its children should
+  already have this flag too.  We can assert this in debug builds.)
+  * To resolve the directionality of the element, we run the algorithm explained
+  in http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-dir-attribute
+  (I'll call this the "downward propagation algorithm".) by walking the child
+  subtree in tree order.  Note that an element with @dir=auto should not affect
+  other elements in its document with @dir=auto.  So there is no need to walk up
+  the parent chain in this case.  TextNodeDirectionalityMap needs to be updated
+  as appropriate.
+
+  3a. When the dir attribute is set to any valid value on an element that didn't
+  have a valid dir attribute before, this means that any descendant of that
+  element will not affect the directionality of any of its ancestors. So we need
+  to check whether any text node descendants of the element are listed in
+  TextNodeDirectionalityMap, and whether the elements whose direction they set
+  are ancestors of the element. If so, we need to rerun the downward propagation
+  algorithm for those ancestors.
+
+  4.  When the dir attribute is changed from auto to something else (including
+  the case where it gets removed) on a textarea or an input element with
+  @type=text/search/tel/url/email, we unset the NodeHasDirAuto flag and
+  resolve the directionality based on the directionality of the value of the @dir
+  attribute on element itself or its parent element.
+
+  5. When the dir attribute is changed from auto to something else (including the
+  case where it gets removed) on any element except case 4 above and the bdi
+  element, we run the following algorithm:
+  * We unset the NodeHasDirAuto flag.
+  * If the element does not have the NodeAncestorHasDirAuto flag, we unset
+  the NodeAncestorHasDirAuto flag on all of its child nodes, except those
+  who are a descendant of another element with NodeHasDirAuto.  (Note that if
+  the element has the NodeAncestorHasDirAuto flag, all of its child nodes
+  should still retain the same flag.)
+  * We resolve the directionality of the element based on the value of the @dir
+  attribute on the element itself or its parent element.
+  TextNodeDirectionalityMap needs to be updated as appropriate.
+
+  5a. When the dir attribute is removed or set to an invalid value on any
+  element (except a bdi element) with the NodeAncestorHasDirAuto flag which
+  previously had a valid dir attribute, it might have a text node descendant that
+  did not previously affect the directionality of any of its ancestors but should
+  now begin to affect them.
+  We run the following algorithm:
+  * Walk up the parent chain from the element.
+  * For any element that appears in the TextNodeDirectionalityMap, remove the
+    element from the map and rerun the downward propagation algorithm
+    (see section 3).
+  * If we reach an element without either of the NodeHasDirAuto or
+    NodeAncestorHasDirAuto flags, abort the parent chain walk.
+
+  6. When an element with @dir=auto is added to the document, we should handle it
+  similar to the case 2/3 above.
+
+  7. When an element with NodeHasDirAuto or NodeAncestorHasDirAuto is
+  removed from the document, we should handle it similar to the case 4/5 above,
+  except that we don't need to handle anything in the child subtree.  We should
+  also remove all of the occurrences of that node and its descendants from
+  TextNodeDirectionalityMap. (This is the conceptual description of what needs to
+  happen but in the implementation UnbindFromTree is going to be called on all of
+  the descendants so we don't need to descend into the child subtree).
+
+  8. When the contents of a text node is changed either from script or by the
+  user, we need to run the following algorithm:
+  * If the change has happened after the first character with strong
+  directionality in the text node, do nothing.
+  * If the text node is a child of a bdi, script or style element, do nothing.
+  * If the text node belongs to a textarea with NodeHasDirAuto, we need to
+  update the directionality of the textarea.
+  * Grab a list of elements affected by this text node from
+  TextNodeDirectionalityMap and re-resolve the directionality of each one of them
+  based on the new contents of the text node.
+  * If the text node does not exist in TextNodeDirectionalityMap, and it has the
+  NodeAncestorHasDirAuto flag set, this could potentially be a text node
+  which is going to start affecting the directionality of its parent @dir=auto
+  elements. In this case, we need to fall back to the (potentially expensive)
+  "upward propagation algorithm".  The TextNodeDirectionalityMap data structure
+  needs to be update during this algorithm.
+  * If the new contents of the text node do not have any strong characters, and
+  the old contents used to, and the text node used to exist in
+  TextNodeDirectionalityMap and it has the NodeAncestorHasDirAuto flag set,
+  the elements associated with this text node inside TextNodeDirectionalityMap
+  will now get their directionality from another text node.  In this case, for
+  each element in the list retrieved from TextNodeDirectionalityMap, run the
+  downward propagation algorithm (section 3), and remove the text node from
+  TextNodeDirectionalityMap.
+
+  9. When a new text node is injected into a document, we need to run the
+  following algorithm:
+  * If the contents of the text node do not have any characters with strong
+  direction, do nothing.
+  * If the text node is a child of a bdi, script or style element, do nothing.
+  * If the text node is appended to a textarea element with NodeHasDirAuto, we
+  need to update the directionality of the textarea.
+  * If the text node has NodeAncestorHasDirAuto, we need to run the "upward
+  propagation algorithm".  The TextNodeDirectionalityMap data structure needs to
+  be update during this algorithm.
+
+  10. When a text node is removed from a document, we need to run the following
+  algorithm:
+  * If the contents of the text node do not have any characters with strong
+  direction, do nothing.
+  * If the text node is a child of a bdi, script or style element, do nothing.
+  * If the text node is removed from a textarea element with NodeHasDirAuto,
+  set the directionality to "ltr". (This is what the spec currently says, but I'm
+  filing a spec bug to get it fixed -- the directionality should depend on the
+  parent element here.)
+  * If the text node has NodeAncestorHasDirAuto, we need to look at the list
+  of elements being affected by this text node from TextNodeDirectionalityMap,
+  run the "downward propagation algorithm" (section 3) for each one of them,
+  while updating TextNodeDirectionalityMap along the way.
+
+  11. If the value of the @dir attribute on a bdi element is changed to an
+  invalid value (or if it's removed), determine the new directionality similar
+  to the case 3 above.
+
+  == Implemention Notes ==
+  When a new node gets bound to the tree, the BindToTree function gets called.
+  The reverse case is UnbindFromTree.
+  When the contents of a text node change, nsGenericDOMDataNode::SetTextInternal
+  gets called.
+  */
+
 #include "mozilla/dom/DirectionalityUtils.h"
 #include "nsINode.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "mozilla/dom/Element.h"
-#include "nsIDOMNodeFilter.h"
-#include "nsTreeWalker.h"
 #include "nsIDOMHTMLDocument.h"
-
+#include "nsUnicodeProperties.h"
+#include "nsTextFragment.h"
+#include "nsAttrValue.h"
+#include "nsContentUtils.h"
+#include "nsTextNode.h"
+#include "nsCheapSets.h"
 
 namespace mozilla {
 
-namespace directionality {
+typedef mozilla::dom::Element Element;
+
+/**
+ * Returns true if aNode is one of the elements whose text content should not
+ * affect its own direction, nor the direction of ancestors with dir=auto.
+ *
+ * Note that this does not include <bdi>, whose content does affect its own
+ * direction when it has dir=auto (which it has by default), so one needs to
+ * test for it separately.
+ * It *does* include textarea, because even if a textarea has dir=auto, it has
+ * unicode-bidi: plaintext and is handled automatically in bidi resolution.
+ */
+static bool
+DoesNotParticipateInAutoDirection(const Element* aElement)
+{
+  nsINodeInfo* nodeInfo = aElement->NodeInfo();
+  return (aElement->IsHTML() &&
+          (nodeInfo->Equals(nsGkAtoms::script) ||
+           nodeInfo->Equals(nsGkAtoms::style) ||
+           nodeInfo->Equals(nsGkAtoms::textarea)));
+}
+
+/**
+ * Returns the directionality of a Unicode character
+ */
+static Directionality
+GetDirectionFromChar(uint32_t ch)
+{
+  switch(mozilla::unicode::GetBidiCat(ch)) {
+    case eCharType_RightToLeft:
+    case eCharType_RightToLeftArabic:
+      return eDir_RTL;
+
+    case eCharType_LeftToRight:
+      return eDir_LTR;
+
+    default:
+      return eDir_NotSet;
+  }
+}
+
+inline static bool NodeAffectsDirAutoAncestor(nsINode* aTextNode)
+{
+  Element* parent = aTextNode->GetElementParent();
+  return (parent &&
+          !DoesNotParticipateInAutoDirection(parent) &&
+          parent->NodeOrAncestorHasDirAuto());
+}
+
+/**
+ * Various methods for returning the directionality of a string using the
+ * first-strong algorithm defined in http://unicode.org/reports/tr9/#P2
+ *
+ * @param[out] aFirstStrong the offset to the first character in the string with
+ *             strong directionality, or PR_UINT32_MAX if there is none (return
+               value is eDir_NotSet).
+ * @return the directionality of the string
+ */
+static Directionality
+GetDirectionFromText(const PRUnichar* aText, const uint32_t aLength,
+                     uint32_t* aFirstStrong = nullptr)
+{
+  const PRUnichar* start = aText;
+  const PRUnichar* end = aText + aLength;
+
+  while (start < end) {
+    uint32_t current = start - aText;
+    uint32_t ch = *start++;
+
+    if (NS_IS_HIGH_SURROGATE(ch) &&
+        start < end &&
+        NS_IS_LOW_SURROGATE(*start)) {
+      ch = SURROGATE_TO_UCS4(ch, *start++);
+    }
+
+    Directionality dir = GetDirectionFromChar(ch);
+    if (dir != eDir_NotSet) {
+      if (aFirstStrong) {
+        *aFirstStrong = current;
+      }
+      return dir;
+    }
+  }
+
+  if (aFirstStrong) {
+    *aFirstStrong = PR_UINT32_MAX;
+  }
+  return eDir_NotSet;
+}
+
+static Directionality
+GetDirectionFromText(const char* aText, const uint32_t aLength,
+                        uint32_t* aFirstStrong = nullptr)
+{
+  const char* start = aText;
+  const char* end = aText + aLength;
+
+  while (start < end) {
+    uint32_t current = start - aText;
+    unsigned char ch = (unsigned char)*start++;
+
+    Directionality dir = GetDirectionFromChar(ch);
+    if (dir != eDir_NotSet) {
+      if (aFirstStrong) {
+        *aFirstStrong = current;
+      }
+      return dir;
+    }
+  }
+
+  if (aFirstStrong) {
+    *aFirstStrong = PR_UINT32_MAX;
+  }
+  return eDir_NotSet;
+}
+
+static Directionality
+GetDirectionFromText(const nsTextFragment* aFrag,
+                     uint32_t* aFirstStrong = nullptr)
+{
+  if (aFrag->Is2b()) {
+    return GetDirectionFromText(aFrag->Get2b(), aFrag->GetLength(),
+                                   aFirstStrong);
+  }
+
+  return GetDirectionFromText(aFrag->Get1b(), aFrag->GetLength(),
+                                 aFirstStrong);
+}
+
+/**
+ * Set the directionality of a node with dir=auto as defined in
+ * http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-directionality
+ *
+ * @param[in] aStartAfterNode as an optimization, a caller may pass in a node
+ *            from which to begin walking the descendants of aElement, if it is
+ *            known that all text nodes before this node do not contain any
+ *            strong directional characters
+ * @return the text node containing the character that determined the direction
+ */
+static nsINode*
+WalkDescendantsSetDirectionFromText(Element* aElement, bool aNotify = true,
+                                       nsINode* aStartAfterNode = nullptr)
+{
+  MOZ_ASSERT(aElement, "aElement is null");
 
-typedef mozilla::dom::Element Element;
+  nsIContent* child;
+  if (aStartAfterNode &&
+      nsContentUtils::ContentIsDescendantOf(aStartAfterNode, aElement)) {
+#ifdef DEBUG
+    child = aElement->GetFirstChild();
+    while (child && child != aStartAfterNode) {
+      if (child->NodeType() == nsIDOMNode::TEXT_NODE) {
+        MOZ_ASSERT(GetDirectionFromText(child->GetText()) == eDir_NotSet,
+                   "Strong directional characters before aStartAfterNode");
+      }
+      child = child->GetNextNode(aElement);
+    }
+#endif
+    child = aStartAfterNode->GetNextNode(aElement);
+  } else {
+    child = aElement->GetFirstChild();
+  }
+
+  while (child) {
+    if (child->IsElement() &&
+        (DoesNotParticipateInAutoDirection(child->AsElement()) ||
+         child->NodeInfo()->Equals(nsGkAtoms::bdi) ||
+         child->HasFixedDir())) {
+      child = child->GetNextNonChildNode(aElement);
+      continue;
+    }
+
+    if (child->NodeType() == nsIDOMNode::TEXT_NODE) {
+      Directionality textNodeDir = GetDirectionFromText(child->GetText());
+      if (textNodeDir != eDir_NotSet) {
+        // We found a descendant text node with strong directional characters.
+        // Set the directionality of aElement to the corresponding value.
+        aElement->SetDirectionality(textNodeDir, aNotify);
+        return child;
+      }
+    }
+    child = child->GetNextNode(aElement);
+  }
+
+  // We walked all the descendants without finding a text node with strong
+  // directional characters. Set the directionality to LTR
+  aElement->SetDirectionality(eDir_LTR, aNotify);
+  return nullptr;
+}
+
+class nsTextNodeDirectionalityMap
+{
+  static void
+  nsTextNodeDirectionalityMapDtor(void *aObject, nsIAtom* aPropertyName,
+                                  void *aPropertyValue, void* aData)
+  {
+    nsTextNodeDirectionalityMap* map =
+      reinterpret_cast<nsTextNodeDirectionalityMap * >(aPropertyValue);
+    delete map;
+  }
+
+public:
+  nsTextNodeDirectionalityMap(nsINode* aTextNode)
+  {
+    MOZ_ASSERT(aTextNode, "Null text node");
+    MOZ_COUNT_CTOR(nsTextNodeDirectionalityMap);
+    aTextNode->SetProperty(nsGkAtoms::textNodeDirectionalityMap, this,
+                           nsTextNodeDirectionalityMapDtor);
+    aTextNode->SetHasTextNodeDirectionalityMap();
+  }
+
+  ~nsTextNodeDirectionalityMap()
+  {
+    MOZ_COUNT_DTOR(nsTextNodeDirectionalityMap);
+  }
+
+  void AddEntry(nsINode* aTextNode, Element* aElement)
+  {
+    if (!mElements.Contains(aElement)) {
+      mElements.Put(aElement);
+      aElement->SetProperty(nsGkAtoms::dirAutoSetBy, aTextNode);
+      aElement->SetHasDirAutoSet();
+    }
+  }
+
+  void RemoveEntry(nsINode* aTextNode, Element* aElement)
+  {
+    if (mElements.Contains(aElement)) {
+      mElements.Remove(aElement);
+
+      aElement->ClearHasDirAutoSet();
+      aElement->UnsetProperty(nsGkAtoms::dirAutoSetBy);
+    }
+  }
+
+private:
+  nsCheapSet<nsPtrHashKey<Element> > mElements;
+
+  static nsTextNodeDirectionalityMap* GetDirectionalityMap(nsINode* aTextNode)
+  {
+    MOZ_ASSERT(aTextNode->NodeType() == nsIDOMNode::TEXT_NODE,
+               "Must be a text node");
+    nsTextNodeDirectionalityMap* map = nullptr;
+
+    if (aTextNode->HasTextNodeDirectionalityMap()) {
+      map = static_cast<nsTextNodeDirectionalityMap * >
+        (aTextNode->GetProperty(nsGkAtoms::textNodeDirectionalityMap));
+    }
+
+    return map;
+  }
+
+  static PLDHashOperator SetNodeDirection(nsPtrHashKey<Element>* aEntry, void* aDir)
+  {
+    MOZ_ASSERT(aEntry->GetKey()->IsElement(), "Must be an Element");
+    aEntry->GetKey()->SetDirectionality(*reinterpret_cast<Directionality*>(aDir),
+                                        true);
+    return PL_DHASH_NEXT;
+  }
+
+  static PLDHashOperator ResetNodeDirection(nsPtrHashKey<Element>* aEntry, void* aData)
+  {
+    MOZ_ASSERT(aEntry->GetKey()->IsElement(), "Must be an Element");
+    // run the downward propagation algorithm
+    // and remove the text node from the map
+    nsINode* startAfterNode = static_cast<Element*>(aData);
+    Element* rootNode = aEntry->GetKey();
+    nsINode* textNode = WalkDescendantsSetDirectionFromText(rootNode, true,
+                                                            startAfterNode);
+    if (textNode) {
+      nsTextNodeDirectionalityMap::AddEntryToMap(textNode, rootNode);
+    }
+    return PL_DHASH_REMOVE;
+  }
+
+public:
+  void UpdateAutoDirection(Directionality aDir)
+  {
+    mElements.EnumerateEntries(SetNodeDirection, &aDir);
+  }
+
+  void ResetAutoDirection(nsINode* aTextNode, nsINode* aStartAfterNode)
+  {
+    mElements.EnumerateEntries(ResetNodeDirection, aStartAfterNode);
+  }
+
+  static void RemoveElementFromMap(nsINode* aTextNode, Element* aElement)
+  {
+    if (aTextNode->HasTextNodeDirectionalityMap()) {
+      GetDirectionalityMap(aTextNode)->RemoveEntry(aTextNode, aElement);
+    }
+  }
+
+  static void AddEntryToMap(nsINode* aTextNode, Element* aElement)
+  {
+    nsTextNodeDirectionalityMap* map = GetDirectionalityMap(aTextNode);
+    if (!map) {
+      map = new nsTextNodeDirectionalityMap(aTextNode);
+    }
+
+    map->AddEntry(aTextNode, aElement);
+  }
+
+  static void UpdateTextNodeDirection(nsINode* aTextNode, Directionality aDir)
+  {
+    MOZ_ASSERT(aTextNode->HasTextNodeDirectionalityMap(),
+               "Map missing in UpdateTextNodeDirection");
+    GetDirectionalityMap(aTextNode)->UpdateAutoDirection(aDir);
+  }
+
+  static void ResetTextNodeDirection(nsINode* aTextNode,
+                                     nsINode* aStartAfterNode = nullptr)
+  {
+    MOZ_ASSERT(aTextNode->HasTextNodeDirectionalityMap(),
+               "Map missing in ResetTextNodeDirection");
+    GetDirectionalityMap(aTextNode)->ResetAutoDirection(aTextNode,
+                                                        aStartAfterNode);
+  }
+};
 
 Directionality
 RecomputeDirectionality(Element* aElement, bool aNotify)
 {
+  MOZ_ASSERT(!aElement->HasDirAuto(),
+             "RecomputeDirectionality called with dir=auto");
+  if (aElement->HasDirAutoSet()) {
+    nsINode* setByNode =
+      static_cast<nsINode*>(aElement->GetProperty(nsGkAtoms::dirAutoSetBy));
+    if (setByNode) {
+      nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode, aElement);
+    }
+  }
+
   Directionality dir = eDir_LTR;
 
   if (aElement->HasValidDir()) {
     dir = aElement->GetDirectionality();
   } else {
     Element* parent = aElement->GetElementParent();
     if (parent) {
       // If the element doesn't have an explicit dir attribute with a valid
@@ -41,38 +570,303 @@ RecomputeDirectionality(Element* aElemen
       // If there is no parent element, the directionality is the same as the
       // document direction.
       Directionality documentDir =
         aElement->OwnerDoc()->GetDocumentDirectionality();
       if (documentDir != eDir_NotSet) {
         dir = documentDir;
       }
     }
-    
+
     aElement->SetDirectionality(dir, aNotify);
   }
   return dir;
 }
 
 void
 SetDirectionalityOnDescendants(Element* aElement, Directionality aDir,
                                bool aNotify)
 {
   for (nsIContent* child = aElement->GetFirstChild(); child; ) {
     if (!child->IsElement()) {
       child = child->GetNextNode(aElement);
       continue;
     }
 
     Element* element = child->AsElement();
-    if (element->HasValidDir()) {
+    if (element->HasValidDir() || element->HasDirAuto()) {
       child = child->GetNextNonChildNode(aElement);
       continue;
     }
     element->SetDirectionality(aDir, aNotify);
     child = child->GetNextNode(aElement);
   }
 }
 
-} // end namespace directionality
+void
+WalkAncestorsResetAutoDirection(Element* aElement, bool aNotify)
+{
+  nsINode* setByNode;
+  Element* parent = aElement->GetElementParent();
+
+  while (parent && parent->NodeOrAncestorHasDirAuto()) {
+    if (parent->HasDirAutoSet()) {
+      // If the parent has the DirAutoSet flag, its direction is determined by
+      // some text node descendant.
+      // Remove it from the map and reset its direction by the downward
+      // propagation algorithm
+      setByNode =
+        static_cast<nsINode*>(parent->GetProperty(nsGkAtoms::dirAutoSetBy));
+      if (setByNode) {
+        nsTextNodeDirectionalityMap::RemoveElementFromMap(setByNode, parent);
+      }
+    }
+    if (parent->HasDirAuto()) {
+      setByNode = WalkDescendantsSetDirectionFromText(parent, aNotify);
+      if (setByNode) {
+        nsTextNodeDirectionalityMap::AddEntryToMap(setByNode, parent);
+      }
+      break;
+    }
+    parent = parent->GetElementParent();
+  }
+}
+
+void
+WalkDescendantsResetAutoDirection(Element* aElement)
+{
+  nsIContent* child = aElement->GetFirstChild();
+  while (child) {
+    if (child->HasDirAuto()) {
+      child = child->GetNextNonChildNode(aElement);
+      continue;
+    }
+
+    if (child->HasTextNodeDirectionalityMap()) {
+      nsTextNodeDirectionalityMap::ResetTextNodeDirection(child, child);
+    }
+    child = child->GetNextNode(aElement);
+  }
+}
+
+void
+WalkDescendantsSetDirAuto(Element* aElement, bool aNotify)
+{
+  bool setAncestorDirAutoFlag =
+#ifdef DEBUG
+    true;
+#else
+    !aElement->AncestorHasDirAuto();
+#endif
+
+  if (setAncestorDirAutoFlag) {
+    nsIContent* child = aElement->GetFirstChild();
+    while (child) {
+      MOZ_ASSERT(!aElement->AncestorHasDirAuto() ||
+                 child->AncestorHasDirAuto(),
+                 "AncestorHasDirAuto set on node but not its children");
+      child->SetHasDirAuto();
+      child = child->GetNextNode(aElement);
+    }
+  }
+
+  nsINode* textNode = WalkDescendantsSetDirectionFromText(aElement, aNotify);
+  if (textNode) {
+    nsTextNodeDirectionalityMap::AddEntryToMap(textNode, aElement);
+  }
+}
+
+void
+WalkDescendantsClearAncestorDirAuto(Element* aElement)
+{
+  nsIContent* child = aElement->GetFirstChild();
+  while (child) {
+    if (child->HasDirAuto()) {
+      child = child->GetNextNonChildNode(aElement);
+      continue;
+    }
+
+    child->ClearAncestorHasDirAuto();
+    child = child->GetNextNode(aElement);
+  }
+}
+
+void SetAncestorDirectionIfAuto(nsINode* aTextNode, Directionality aDir,
+                                bool aNotify = true)
+{
+  MOZ_ASSERT(aTextNode->NodeType() == nsIDOMNode::TEXT_NODE,
+             "Must be a text node");
+
+  Element* parent = aTextNode->GetElementParent();
+  while (parent && parent->NodeOrAncestorHasDirAuto()) {
+    if (DoesNotParticipateInAutoDirection(parent) || parent->HasFixedDir()) {
+      break;
+    }
+
+    if (parent->HasDirAuto()) {
+      bool resetDirection = false;
+
+      if (!parent->HasDirAutoSet()) {
+        // Fast path if parent's direction is not yet set by any descendant
+        resetDirection = true;
+      } else {
+        // If parent's direction is already set, we need to know if
+        // aTextNode is before or after the text node that had set it.
+        // We will walk parent's descendants in tree order starting from
+        // aTextNode to optimize for the most common case where text nodes are
+        // being appended to tree.
+        nsINode* directionWasSetByTextNode =
+          static_cast<nsINode*>(parent->GetProperty(nsGkAtoms::dirAutoSetBy));
+        if (!directionWasSetByTextNode) {
+          resetDirection = true;
+        } else if (directionWasSetByTextNode != aTextNode) {
+          nsIContent* child = aTextNode->GetNextNode(parent);
+          while (child) {
+            if (child->IsElement() &&
+                (DoesNotParticipateInAutoDirection(child->AsElement()) ||
+                 child->NodeInfo()->Equals(nsGkAtoms::bdi) ||
+                 child->HasFixedDir())) {
+              child = child->GetNextNonChildNode(parent);
+              continue;
+            }
+
+            if (child == directionWasSetByTextNode) {
+              // we found the node that set the element's direction after our
+              // text node, so we need to reset the direction
+              resetDirection = true;
+              break;
+            }
+
+            child = child->GetNextNode(parent);
+          }
+        }
+      }
+
+      if (resetDirection) {
+        parent->SetDirectionality(aDir, aNotify);
+        nsTextNodeDirectionalityMap::AddEntryToMap(aTextNode, parent);
+        SetDirectionalityOnDescendants(parent, aDir, aNotify);
+      }
+
+      // Since we found an element with dir=auto, we can stop walking the
+      // parent chain: none of its ancestors will have their direction set by
+      // any of its descendants.
+      return;
+    }
+    parent = parent->GetElementParent();
+  }
+}
+
+void
+SetDirectionFromChangedTextNode(nsIContent* aTextNode, uint32_t aOffset,
+                                const PRUnichar* aBuffer, uint32_t aLength,
+                                bool aNotify)
+{
+  if (!NodeAffectsDirAutoAncestor(aTextNode)) {
+    return;
+  }
+
+  uint32_t firstStrong;
+  Directionality oldDir = GetDirectionFromText(aTextNode->GetText(),
+                                               &firstStrong);
+  if (aOffset > firstStrong) {
+    return;
+  }
+
+  Directionality newDir = GetDirectionFromText(aBuffer, aLength);
+  if (newDir == eDir_NotSet) {
+    if (oldDir != eDir_NotSet && aTextNode->HasTextNodeDirectionalityMap()) {
+      // This node used to have a strong directional character but no
+      // longer does. ResetTextNodeDirection() will re-resolve the
+      // directionality of any elements whose directionality was
+      // determined by this node.
+      nsTextNodeDirectionalityMap::ResetTextNodeDirection(aTextNode);
+    }
+  } else {
+    // This node has a strong directional character. If it has a
+    // TextNodeDirectionalityMap property, it already determines the
+    // directionality of some element(s), so call UpdateTextNodeDirection to
+    // reresolve their directionality. Otherwise call
+    // SetAncestorDirectionIfAuto to find ancestor elements which should
+    // have their directionality determined by this node.
+    if (aTextNode->HasTextNodeDirectionalityMap()) {
+      nsTextNodeDirectionalityMap::UpdateTextNodeDirection(aTextNode, newDir);
+    } else {
+      SetAncestorDirectionIfAuto(aTextNode, newDir, aNotify);
+    }
+  }
+}
+
+void
+SetDirectionFromNewTextNode(nsTextNode* aTextNode)
+{
+  if (!NodeAffectsDirAutoAncestor(aTextNode)) {
+    return;
+  }
+
+  Directionality dir = GetDirectionFromText(aTextNode->GetText());
+  if (dir != eDir_NotSet) {
+    SetAncestorDirectionIfAuto(aTextNode, dir);
+  }
+}
+
+void
+ResetDirectionSetByTextNode(nsTextNode* aTextNode)
+{
+  if (!NodeAffectsDirAutoAncestor(aTextNode)) {
+    return;
+  }
+
+  Directionality dir = GetDirectionFromText(aTextNode->GetText());
+  if (dir != eDir_NotSet && aTextNode->HasTextNodeDirectionalityMap()) {
+    nsTextNodeDirectionalityMap::ResetTextNodeDirection(aTextNode);
+  }
+}
+
+void
+SetDirectionalityFromValue(Element* aElement, const nsAString& value,
+                           bool aNotify)
+{
+  Directionality dir = GetDirectionFromText(PromiseFlatString(value).get(),
+                                            value.Length());
+  if (dir == eDir_NotSet) {
+    dir = eDir_LTR;
+  }
+
+  aElement->SetDirectionality(dir, aNotify);
+}
+
+void
+OnSetDirAttr(Element* aElement, const nsAttrValue* aNewValue,
+             bool hadValidDir, bool aNotify)
+{
+  if (aElement->IsHTML() && aElement->NodeInfo()->Equals(nsGkAtoms::input)) {
+    return;
+  }
+
+  if (aElement->AncestorHasDirAuto()) {
+    if (!hadValidDir) {
+      // The element is a descendant of an element with dir = auto, is
+      // having its dir attribute set, and previously didn't have a valid dir
+      // attribute.
+      // Check whether any of its text node descendants determine the
+      // direction of any of its ancestors, and redetermine their direction
+      WalkDescendantsResetAutoDirection(aElement);
+    } else if (!aElement->HasValidDir()) {
+      // The element is a descendant of an element with dir = auto and is
+      // having its dir attribute removed or set to an invalid value.
+      // Reset the direction of any of its ancestors whose direction is
+      // determined by a text node descendant
+      WalkAncestorsResetAutoDirection(aElement, aNotify);
+    }
+  }
+
+  if (aElement->HasDirAuto()) {
+    WalkDescendantsSetDirAuto(aElement, aNotify);
+  } else {
+    SetDirectionalityOnDescendants(aElement,
+                                   RecomputeDirectionality(aElement, aNotify),
+                                   aNotify);
+  }
+}
 
 } // end namespace mozilla
 
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -125,17 +125,16 @@
 
 #include "mozilla/CORSMode.h"
 
 #include "nsStyledElement.h"
 #include "nsXBLService.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
-using namespace mozilla::directionality;
 
 nsEventStates
 Element::IntrinsicState() const
 {
   return IsEditable() ? NS_EVENT_STATE_MOZ_READWRITE :
                         NS_EVENT_STATE_MOZ_READONLY;
 }
 
@@ -1158,17 +1157,31 @@ Element::BindToTree(nsIDocument* aDocume
     // If we're not in the doc, update our subtree pointer.
     SetSubtreeRootPointer(aParent->SubtreeRoot());
   }
 
   // This has to be here, rather than in nsGenericHTMLElement::BindToTree, 
   //  because it has to happen after updating the parent pointer, but before
   //  recursively binding the kids.
   if (IsHTML()) {
-    RecomputeDirectionality(this, false);
+    if (aParent && aParent->NodeOrAncestorHasDirAuto()) {
+      SetAncestorHasDirAuto();
+      // if we are binding an element to the tree that already has descendants,
+      // and the parent has NodeHasDirAuto or NodeAncestorHasDirAuto, we may
+      // need to reset the direction of an ancestor with dir=auto
+      if (GetFirstChild()) {
+        WalkAncestorsResetAutoDirection(this);
+      }
+    }
+
+    if (!HasDirAuto()) {
+      // if the element doesn't have dir=auto, set its directionality from
+      // the dir attribute or by inheriting from its ancestors.
+      RecomputeDirectionality(this, false);
+    }
   }
 
   // If NODE_FORCE_XBL_BINDINGS was set we might have anonymous children
   // that also need to be told that they are moving.
   nsresult rv;
   if (hadForceXBL) {
     nsBindingManager* bmgr = OwnerDoc()->BindingManager();
 
@@ -1349,17 +1362,17 @@ Element::UnbindFromTree(bool aDeep, bool
     if (slots) {
       slots->mBindingParent = nullptr;
     }
   }
 
   // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree, 
   //  because it has to happen after unsetting the parent pointer, but before
   //  recursively unbinding the kids.
-  if (IsHTML()) {
+  if (IsHTML() && !HasDirAuto()) {
     RecomputeDirectionality(this, false);
   }
 
   if (aDeep) {
     // Do the kids. Don't call GetChildCount() here since that'll force
     // XUL to generate template children, which there is no need for since
     // all we're going to do is unbind them anyway.
     uint32_t i, n = mAttrsAndChildren.ChildCount();
@@ -1822,17 +1835,23 @@ Element::SetAttrAndNotify(int32_t aNames
 
   // Copy aParsedValue for later use since it will be lost when we call
   // SetAndTakeMappedAttr below
   nsAttrValue aValueForAfterSetAttr;
   if (aCallAfterSetAttr) {
     aValueForAfterSetAttr.SetTo(aParsedValue);
   }
 
+  bool hadValidDir = false;
+
   if (aNamespaceID == kNameSpaceID_None) {
+    if (aName == nsGkAtoms::dir) {
+      hadValidDir = HasValidDir() || NodeInfo()->Equals(nsGkAtoms::bdi);
+    }
+
     // XXXbz Perhaps we should push up the attribute mapping function
     // stuff to Element?
     if (!IsAttributeMapped(aName) ||
         !SetMappedAttribute(document, aName, aParsedValue, &rv)) {
       rv = mAttrsAndChildren.SetAndTakeAttr(aName, aParsedValue);
     }
   }
   else {
@@ -1858,16 +1877,20 @@ Element::SetAttrAndNotify(int32_t aNames
 
   if (aNotify) {
     nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType);
   }
 
   if (aCallAfterSetAttr) {
     rv = AfterSetAttr(aNamespaceID, aName, &aValueForAfterSetAttr, aNotify);
     NS_ENSURE_SUCCESS(rv, rv);
+
+    if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
+      OnSetDirAttr(this, &aValueForAfterSetAttr, hadValidDir, aNotify);
+    }
   }
 
   if (aFireMutation) {
     nsMutationEvent mutation(true, NS_MUTATION_ATTRMODIFIED);
 
     nsAutoString ns;
     nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
     ErrorResult rv;
@@ -2060,16 +2083,22 @@ Element::UnsetAttr(int32_t aNameSpaceID,
   if (slots && slots->mAttributeMap) {
     slots->mAttributeMap->DropAttribute(aNameSpaceID, aName);
   }
 
   // The id-handling code, and in the future possibly other code, need to
   // react to unexpected attribute changes.
   nsMutationGuard::DidMutate();
 
+  bool hadValidDir = false;
+
+  if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
+    hadValidDir = HasValidDir() || NodeInfo()->Equals(nsGkAtoms::bdi);
+  }
+
   nsAttrValue oldValue;
   rv = mAttrsAndChildren.RemoveAttrAt(index, oldValue);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
     nsRefPtr<nsXBLBinding> binding =
       OwnerDoc()->BindingManager()->GetBinding(this);
     if (binding) {
@@ -2082,16 +2111,20 @@ Element::UnsetAttr(int32_t aNameSpaceID,
   if (aNotify) {
     nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
                                   nsIDOMMutationEvent::REMOVAL);
   }
 
   rv = AfterSetAttr(aNameSpaceID, aName, nullptr, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
+    OnSetDirAttr(this, nullptr, hadValidDir, aNotify);
+  }
+
   if (hasMutationListeners) {
     nsCOMPtr<nsIDOMEventTarget> node = do_QueryObject(this);
     nsMutationEvent mutation(true, NS_MUTATION_ATTRMODIFIED);
 
     mutation.mRelatedNode = attrNode;
     mutation.mAttrName = aName;
 
     nsAutoString value;
@@ -2480,17 +2513,22 @@ ParseSelectorList(nsINode* aNode,
   nsIDocument* doc = aNode->OwnerDoc();
   nsCSSParser parser(doc->CSSLoader());
 
   nsCSSSelectorList* selectorList;
   nsresult rv = parser.ParseSelectorString(aSelectorString,
                                            doc->GetDocumentURI(),
                                            0, // XXXbz get the line number!
                                            &selectorList);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) {
+    // We hit this for syntax errors, which are quite common, so don't
+    // use NS_ENSURE_SUCCESS.  (For example, jQuery has an extended set
+    // of selectors, but it sees if we can parse them first.)
+    return rv;
+  }
 
   // Filter out pseudo-element selectors from selectorList
   nsCSSSelectorList** slot = &selectorList;
   do {
     nsCSSSelectorList* cur = *slot;
     if (cur->mSelectors->IsPseudoElement()) {
       *slot = cur->mNext;
       cur->mNext = nullptr;
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -164,16 +164,17 @@
 #include "nsIEditor.h"
 #include "nsIEditorDocShell.h"
 #include "mozilla/Attributes.h"
 #include "nsIParserService.h"
 #include "nsIDOMScriptObjectFactory.h"
 #include "nsSandboxFlags.h"
 #include "nsSVGFeatures.h"
 #include "MediaDecoder.h"
+#include "DecoderTraits.h"
 
 #include "nsWrapperCacheInlines.h"
 
 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
                                       const char** next, PRUnichar* result);
 extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end,
                                  int ns_aware, const char** colon);
 
@@ -6586,48 +6587,48 @@ nsContentUtils::FindInternalContentViewe
       else
       *aLoaderType = TYPE_UNKNOWN;
     }
     return docFactory.forget();
   }
 
 #ifdef MOZ_MEDIA
 #ifdef MOZ_OGG
-  if (nsHTMLMediaElement::IsOggType(nsDependentCString(aType))) {
+  if (DecoderTraits::IsOggType(nsDependentCString(aType))) {
     docFactory = do_GetService("@mozilla.org/content/document-loader-factory;1");
     if (docFactory && aLoaderType) {
       *aLoaderType = TYPE_CONTENT;
     }
     return docFactory.forget();
   }
 #endif
 
 #ifdef MOZ_WEBM
-  if (nsHTMLMediaElement::IsWebMType(nsDependentCString(aType))) {
+  if (DecoderTraits::IsWebMType(nsDependentCString(aType))) {
     docFactory = do_GetService("@mozilla.org/content/document-loader-factory;1");
     if (docFactory && aLoaderType) {
       *aLoaderType = TYPE_CONTENT;
     }
     return docFactory.forget();
   }
 #endif
 
 #ifdef MOZ_GSTREAMER
-  if (nsHTMLMediaElement::IsGStreamerSupportedType(nsDependentCString(aType))) {
+  if (DecoderTraits::IsGStreamerSupportedType(nsDependentCString(aType))) {
     docFactory = do_GetService("@mozilla.org/content/document-loader-factory;1");
     if (docFactory && aLoaderType) {
       *aLoaderType = TYPE_CONTENT;
     }
     return docFactory.forget();
   }
 #endif
 
 #ifdef MOZ_MEDIA_PLUGINS
   if (mozilla::MediaDecoder::IsMediaPluginsEnabled() &&
-      nsHTMLMediaElement::IsMediaPluginsType(nsDependentCString(aType))) {
+      DecoderTraits::IsMediaPluginsType(nsDependentCString(aType))) {
     docFactory = do_GetService("@mozilla.org/content/document-loader-factory;1");
     if (docFactory && aLoaderType) {
       *aLoaderType = TYPE_CONTENT;
     }
     return docFactory.forget();
   }
 #endif // MOZ_MEDIA_PLUGINS
 #endif // MOZ_MEDIA
--- a/content/base/src/nsCrossSiteListenerProxy.cpp
+++ b/content/base/src/nsCrossSiteListenerProxy.cpp
@@ -504,20 +504,21 @@ nsCORSListenerProxy::CheckRequestApprove
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!allowCredentialsHeader.EqualsLiteral("true")) {
       return NS_ERROR_DOM_BAD_URI;
     }
   }
 
   if (mIsPreflight) {
-    bool succeeded;
-    rv = http->GetRequestSucceeded(&succeeded);
+    // Preflights only succeed if the response has a 200 status
+    uint32_t status;
+    rv = http->GetResponseStatus(&status);
     NS_ENSURE_SUCCESS(rv, rv);
-    if (!succeeded) {
+    if (status != 200) {
       return NS_ERROR_DOM_BAD_URI;
     }
 
     nsAutoCString headerVal;
     // The "Access-Control-Allow-Methods" header contains a comma separated
     // list of method names.
     http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
                             headerVal);
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -172,17 +172,16 @@
 
 #include "imgILoader.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsSandboxFlags.h"
 #include "nsIAppsService.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
-using namespace mozilla::directionality;
 
 typedef nsTArray<Link*> LinkArray;
 
 // Reference to the document which requested DOM full-screen mode.
 nsWeakPtr nsDocument::sFullScreenDoc = nullptr;
 
 // Reference to the root document of the branch containing the document
 // which requested DOM full-screen mode.
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -1048,17 +1048,17 @@ protected:
 
   // Refreshes the hrefs of all the links in the document.
   void RefreshLinkHrefs();
 
   nsIContent* GetFirstBaseNodeWithHref();
   nsresult SetFirstBaseNodeWithHref(nsIContent *node);
 
   inline void
-  SetDocumentDirectionality(mozilla::directionality::Directionality aDir)
+  SetDocumentDirectionality(mozilla::Directionality aDir)
   {
     mDirectionality = aDir;
   }
 
   // Get the first <title> element with the given IsNodeOfType type, or
   // return null if there isn't one
   nsIContent* GetTitleContent(uint32_t aNodeType);
   // Find the first "title" element in the given IsNodeOfType type and
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -21,16 +21,17 @@
 #include "nsIDOMText.h"
 #include "nsCOMPtr.h"
 #include "nsDOMString.h"
 #include "nsIDOMUserDataHandler.h"
 #include "nsChangeHint.h"
 #include "nsEventDispatcher.h"
 #include "nsCOMArray.h"
 #include "nsNodeUtils.h"
+#include "mozilla/dom/DirectionalityUtils.h"
 #include "nsBindingManager.h"
 #include "nsCCUncollectableMarker.h"
 #include "mozAutoDocUpdate.h"
 #include "nsAsyncDOMEvent.h"
 
 #include "pldhash.h"
 #include "prprf.h"
 #include "nsWrapperCacheInlines.h"
@@ -274,16 +275,20 @@ nsGenericDOMDataNode::SetTextInternal(ui
       aOffset,
       endOffset,
       aLength,
       aDetails
     };
     nsNodeUtils::CharacterDataWillChange(this, &info);
   }
 
+  if (NodeType() == nsIDOMNode::TEXT_NODE) {
+    SetDirectionFromChangedTextNode(this, aOffset, aBuffer, aLength, aNotify);
+  }
+
   if (aOffset == 0 && endOffset == textLength) {
     // Replacing whole text or old text was empty.  Don't bother to check for
     // bidi in this string if the document already has bidi enabled.
     mText.SetTo(aBuffer, aLength, !document || !document->GetBidiEnabled());
   }
   else if (aOffset == textLength) {
     // Appending to existing
     mText.Append(aBuffer, aLength, !document || !document->GetBidiEnabled());
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -271,16 +271,17 @@ GK_ATOM(details, "details")
 GK_ATOM(deviceAspectRatio, "device-aspect-ratio")
 GK_ATOM(deviceHeight, "device-height")
 GK_ATOM(deviceWidth, "device-width")
 GK_ATOM(dfn, "dfn")
 GK_ATOM(dialog, "dialog")
 GK_ATOM(difference, "difference")
 GK_ATOM(digit, "digit")
 GK_ATOM(dir, "dir")
+GK_ATOM(dirAutoSetBy, "dirAutoSetBy")
 GK_ATOM(directionality, "directionality")
 GK_ATOM(disableOutputEscaping, "disable-output-escaping")
 GK_ATOM(disabled, "disabled")
 GK_ATOM(display, "display")
 GK_ATOM(distinct, "distinct")
 GK_ATOM(div, "div")
 GK_ATOM(dl, "dl")
 GK_ATOM(doctypePublic, "doctype-public")
@@ -1028,16 +1029,17 @@ GK_ATOM(td, "td")
 GK_ATOM(_template, "template")
 GK_ATOM(text_decoration, "text-decoration")
 GK_ATOM(terminate, "terminate")
 GK_ATOM(test, "test")
 GK_ATOM(text, "text")
 GK_ATOM(textarea, "textarea")
 GK_ATOM(textbox, "textbox")
 GK_ATOM(textnode, "textnode")
+GK_ATOM(textNodeDirectionalityMap, "textNodeDirectionalityMap")
 GK_ATOM(tfoot, "tfoot")
 GK_ATOM(th, "th")
 GK_ATOM(thead, "thead")
 GK_ATOM(thumb, "thumb")
 GK_ATOM(time, "time")
 GK_ATOM(title, "title")
 GK_ATOM(titlebar, "titlebar")
 GK_ATOM(titletip, "titletip")
@@ -1938,16 +1940,17 @@ GK_ATOM(el, "el")
 GK_ATOM(TypingTxnName, "Typing")
 GK_ATOM(IMETxnName, "IME")
 GK_ATOM(DeleteTxnName, "Deleting")
 
 // IPC stuff
 GK_ATOM(Remote, "remote")
 GK_ATOM(RemoteId, "_remote_id")
 GK_ATOM(DisplayPort, "_displayport")
+GK_ATOM(CriticalDisplayPort, "_critical_displayport")
 
 // Names for system metrics
 GK_ATOM(scrollbar_start_backward, "scrollbar-start-backward")
 GK_ATOM(scrollbar_start_forward, "scrollbar-start-forward")
 GK_ATOM(scrollbar_end_backward, "scrollbar-end-backward")
 GK_ATOM(scrollbar_end_forward, "scrollbar-end-forward")
 GK_ATOM(scrollbar_thumb_proportional, "scrollbar-thumb-proportional")
 GK_ATOM(images_in_menus, "images-in-menus")
--- a/content/base/src/nsNodeInfoManager.cpp
+++ b/content/base/src/nsNodeInfoManager.cpp
@@ -127,17 +127,17 @@ nsNodeInfoManager::~nsNodeInfoManager()
 #endif
 
   nsLayoutStatics::Release();
 }
 
 
 NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(nsNodeInfoManager)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_NATIVE_0(nsNodeInfoManager)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsNodeInfoManager)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNodeInfoManager)
   if (tmp->mDocument &&
       nsCCUncollectableMarker::InGeneration(cb,
                                             tmp->mDocument->GetMarkedCCGeneration())) {
     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   }
   if (tmp->mNonDocumentNodeInfos) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDocument)
   }
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -522,20 +522,17 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
 
     if (elem) {
       elem->RecompileScriptEventListeners();
     }
 
     if (aCx && wrapper) {
       nsIXPConnect *xpc = nsContentUtils::XPConnect();
       if (xpc) {
-        nsCOMPtr<nsIXPConnectJSObjectHolder> oldWrapper;
-        rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode,
-                                               getter_AddRefs(oldWrapper));
-
+        rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode);
         if (NS_FAILED(rv)) {
           aNode->mNodeInfo.swap(nodeInfo);
 
           return rv;
         }
       }
     }
   }
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -554,17 +554,17 @@ nsRange::ContentAppended(nsIDocument* aD
       }
       child = child->GetNextSibling();
     }
   }
 
   if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
     MOZ_ASSERT(mAssertNextInsertOrAppendIndex == aNewIndexInContainer);
     MOZ_ASSERT(mAssertNextInsertOrAppendNode == aFirstNewContent);
-    MOZ_ASSERT(aFirstNewContent->IsNodeOfType(nsINode::eTEXT));
+    MOZ_ASSERT(aFirstNewContent->IsNodeOfType(nsINode::eDATA_NODE));
     mStartOffsetWasIncremented = mEndOffsetWasIncremented = false;
 #ifdef DEBUG
     mAssertNextInsertOrAppendIndex = -1;
     mAssertNextInsertOrAppendNode = nullptr;
 #endif
   }
 }
 
@@ -591,17 +591,17 @@ nsRange::ContentInserted(nsIDocument* aD
       !aChild->IsDescendantOfCommonAncestorForRangeInSelection()) {
     MarkDescendants(aChild);
     aChild->SetDescendantOfCommonAncestorForRangeInSelection();
   }
 
   if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
     MOZ_ASSERT(mAssertNextInsertOrAppendIndex == aIndexInContainer);
     MOZ_ASSERT(mAssertNextInsertOrAppendNode == aChild);
-    MOZ_ASSERT(aChild->IsNodeOfType(nsINode::eTEXT));
+    MOZ_ASSERT(aChild->IsNodeOfType(nsINode::eDATA_NODE));
     mStartOffsetWasIncremented = mEndOffsetWasIncremented = false;
 #ifdef DEBUG
     mAssertNextInsertOrAppendIndex = -1;
     mAssertNextInsertOrAppendNode = nullptr;
 #endif
   }
 }
 
--- a/content/base/src/nsTextNode.cpp
+++ b/content/base/src/nsTextNode.cpp
@@ -4,25 +4,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Implementation of DOM Core's nsIDOMText node.
  */
 
 #include "nsTextNode.h"
 #include "nsContentUtils.h"
+#include "mozilla/dom/DirectionalityUtils.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIDocument.h"
 #include "nsThreadUtils.h"
 #include "nsStubMutationObserver.h"
 #ifdef DEBUG
 #include "nsRange.h"
 #endif
 
+using namespace mozilla;
 using namespace mozilla::dom;
 
 /**
  * class used to implement attr() generated content
  */
 class nsAttributeTextNode : public nsTextNode,
                             public nsStubMutationObserver
 {
@@ -157,16 +159,37 @@ nsTextNode::AppendTextForNormalize(const
                                    bool aNotify, nsIContent* aNextSibling)
 {
   CharacterDataChangeInfo::Details details = {
     CharacterDataChangeInfo::Details::eMerge, aNextSibling
   };
   return SetTextInternal(mText.GetLength(), 0, aBuffer, aLength, aNotify, &details);
 }
 
+nsresult
+nsTextNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+                       nsIContent* aBindingParent, bool aCompileEventHandlers)
+{
+  nsresult rv = nsGenericDOMDataNode::BindToTree(aDocument, aParent,
+                                                 aBindingParent,
+                                                 aCompileEventHandlers);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  SetDirectionFromNewTextNode(this);
+
+  return NS_OK;
+}
+
+void nsTextNode::UnbindFromTree(bool aDeep, bool aNullParent)
+{
+  ResetDirectionSetByTextNode(this);
+
+  nsGenericDOMDataNode::UnbindFromTree(aDeep, aNullParent);
+}
+
 #ifdef DEBUG
 void
 nsTextNode::List(FILE* out, int32_t aIndent) const
 {
   int32_t index;
   for (index = aIndent; --index >= 0; ) fputs("  ", out);
 
   fprintf(out, "Text@%p", static_cast<const void*>(this));
--- a/content/base/src/nsTextNode.h
+++ b/content/base/src/nsTextNode.h
@@ -35,16 +35,22 @@ public:
   // nsINode
   virtual bool IsNodeOfType(uint32_t aFlags) const;
 
   virtual nsGenericDOMDataNode* CloneDataNode(nsINodeInfo *aNodeInfo,
                                               bool aCloneText) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 
+  virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+                              nsIContent* aBindingParent,
+                              bool aCompileEventHandlers);
+  virtual void UnbindFromTree(bool aDeep = true,
+                              bool aNullParent = true);
+
   nsresult AppendTextForNormalize(const PRUnichar* aBuffer, uint32_t aLength,
                                   bool aNotify, nsIContent* aNextSibling);
 
   virtual nsIDOMNode* AsDOMNode() { return this; }
 
 #ifdef DEBUG
   virtual void List(FILE* out, int32_t aIndent) const;
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const;
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -98,17 +98,16 @@ using namespace mozilla::dom;
 
 // State
 #define XML_HTTP_REQUEST_UNSENT           (1 << 0) // 0 UNSENT
 #define XML_HTTP_REQUEST_OPENED           (1 << 1) // 1 OPENED
 #define XML_HTTP_REQUEST_HEADERS_RECEIVED (1 << 2) // 2 HEADERS_RECEIVED
 #define XML_HTTP_REQUEST_LOADING          (1 << 3) // 3 LOADING
 #define XML_HTTP_REQUEST_DONE             (1 << 4) // 4 DONE
 #define XML_HTTP_REQUEST_SENT             (1 << 5) // Internal, OPENED in IE and external view
-#define XML_HTTP_REQUEST_STOPPED          (1 << 6) // Internal, LOADING in IE and external view
 // The above states are mutually exclusive, change with ChangeState() only.
 // The states below can be combined.
 #define XML_HTTP_REQUEST_ABORTED        (1 << 7)  // Internal
 #define XML_HTTP_REQUEST_ASYNC          (1 << 8)  // Internal
 #define XML_HTTP_REQUEST_PARSEBODY      (1 << 9)  // Internal
 #define XML_HTTP_REQUEST_SYNCLOOPING    (1 << 10) // Internal
 #define XML_HTTP_REQUEST_MULTIPART      (1 << 11) // Internal
 #define XML_HTTP_REQUEST_GOT_FINAL_STOP (1 << 12) // Internal
@@ -123,18 +122,17 @@ using namespace mozilla::dom;
 #define XML_HTTP_REQUEST_DELETED (1 << 19) // Internal
 
 #define XML_HTTP_REQUEST_LOADSTATES         \
   (XML_HTTP_REQUEST_UNSENT |                \
    XML_HTTP_REQUEST_OPENED |                \
    XML_HTTP_REQUEST_HEADERS_RECEIVED |      \
    XML_HTTP_REQUEST_LOADING |               \
    XML_HTTP_REQUEST_DONE |                  \
-   XML_HTTP_REQUEST_SENT |                  \
-   XML_HTTP_REQUEST_STOPPED)
+   XML_HTTP_REQUEST_SENT)
 
 #define NS_BADCERTHANDLER_CONTRACTID \
   "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
 
 #define NS_PROGRESS_EVENT_INTERVAL 50
 
 #define IMPL_STRING_GETTER(_name)                                               \
   NS_IMETHODIMP                                                                 \
@@ -415,18 +413,17 @@ nsXMLHttpRequest::nsXMLHttpRequest()
   StaticAssertions();
 #endif
 }
 
 nsXMLHttpRequest::~nsXMLHttpRequest()
 {
   mState |= XML_HTTP_REQUEST_DELETED;
 
-  if (mState & (XML_HTTP_REQUEST_STOPPED |
-                XML_HTTP_REQUEST_SENT |
+  if (mState & (XML_HTTP_REQUEST_SENT |
                 XML_HTTP_REQUEST_LOADING)) {
     Abort();
   }
 
   NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
 
   nsLayoutStatics::Release();
@@ -1765,18 +1762,17 @@ nsXMLHttpRequest::Open(const nsACString&
   }
 
   nsresult rv;
   nsCOMPtr<nsIURI> uri;
 
   if (mState & (XML_HTTP_REQUEST_OPENED |
                 XML_HTTP_REQUEST_HEADERS_RECEIVED |
                 XML_HTTP_REQUEST_LOADING |
-                XML_HTTP_REQUEST_SENT |
-                XML_HTTP_REQUEST_STOPPED)) {
+                XML_HTTP_REQUEST_SENT)) {
     // IE aborts as well
     Abort();
 
     // XXX We should probably send a warning to the JS console
     //     that load was aborted and event listeners were cleared
     //     since this looks like a situation that could happen
     //     by accident and you could spend a lot of time wondering
     //     why things didn't work.
@@ -3341,17 +3337,17 @@ nsXMLHttpRequest::ReadyState()
     return UNSENT;
   }
   if (mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
     return OPENED;
   }
   if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
     return HEADERS_RECEIVED;
   }
-  if (mState & (XML_HTTP_REQUEST_LOADING | XML_HTTP_REQUEST_STOPPED)) {
+  if (mState & XML_HTTP_REQUEST_LOADING) {
     return LOADING;
   }
   MOZ_ASSERT(mState & XML_HTTP_REQUEST_DONE);
   return DONE;
 }
 
 /* void overrideMimeType(in DOMString mimetype); */
 NS_IMETHODIMP
@@ -3500,17 +3496,18 @@ nsXMLHttpRequest::ChangeState(uint32_t a
   nsresult rv = NS_OK;
 
   if (mProgressNotifier &&
       !(aState & (XML_HTTP_REQUEST_HEADERS_RECEIVED | XML_HTTP_REQUEST_LOADING))) {
     mProgressTimerIsActive = false;
     mProgressNotifier->Cancel();
   }
 
-  if ((aState & XML_HTTP_REQUEST_LOADSTATES) && // Broadcast load states only
+  if ((aState & XML_HTTP_REQUEST_LOADSTATES) &&  // Broadcast load states only
+      aState != XML_HTTP_REQUEST_SENT && // And not internal ones
       aBroadcast &&
       (mState & XML_HTTP_REQUEST_ASYNC ||
        aState & XML_HTTP_REQUEST_OPENED ||
        aState & XML_HTTP_REQUEST_DONE)) {
     nsCOMPtr<nsIDOMEvent> event;
     rv = CreateReadystatechangeEvent(getter_AddRefs(event));
     NS_ENSURE_SUCCESS(rv, rv);
 
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -590,16 +590,18 @@ MOCHITEST_FILES_B = \
     file_mixed_content_main_bug803225_websocket_wsh.py \
     bug803225_test_mailto.html \
 		test_bug789856.html \
 		file_bug804395.jar \
 		test_bug804395.html \
 		test_bug809003.html \
 		test_textnode_split_in_selection.html \
 		test_textnode_normalize_in_selection.html \
+		test_xhr_send_readystate.html \
+		test_bug813919.html \
 		$(NULL)
 
 # OOP tests don't work on Windows (bug 763081) or native-fennec
 # (see Bug 774939)
 ifneq ($(OS_ARCH),WINNT)
 ifndef MOZ_ANDROID_OMTC
 MOCHITEST_FILES_B += \
 		test_messagemanager_assertpermission.html \
--- a/content/base/test/test_CrossSiteXHR.html
+++ b/content/base/test/test_CrossSiteXHR.html
@@ -497,16 +497,22 @@ function runTest() {
                  preflightStatus: 400
                },
                { pass: 1,
                  method: "GET",
                  headers: { "x-my-header": "header value" },
                  allowHeaders: "x-my-header",
                  preflightStatus: 200
                },
+               { pass: 0,
+                 method: "GET",
+                 headers: { "x-my-header": "header value" },
+                 allowHeaders: "x-my-header",
+                 preflightStatus: 204
+               },
 
                // exposed headers
                { pass: 1,
                  method: "GET",
                  responseHeaders: { "x-my-header": "x header" },
                  exposeHeaders: "x-my-header",
                  expectedResponseHeaders: ["x-my-header"],
                },
@@ -646,26 +652,26 @@ function runTest() {
         is(res.statusText, "OK", "wrong status text for " + test.toSource());
       }
       if (test.method !== "HEAD") {
         is(res.responseXML, "<res>hello pass</res>",
            "wrong responseXML in test for " + test.toSource());
         is(res.responseText, "<res>hello pass</res>\n",
            "wrong responseText in test for " + test.toSource());
         is(res.events.join(","),
-           "opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load,loadend",
+           "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
            "wrong responseText in test for " + test.toSource());
       }
       else {
         is(res.responseXML, null,
            "wrong responseXML in test for " + test.toSource());
         is(res.responseText, "",
            "wrong responseText in test for " + test.toSource());
         is(res.events.join(","),
-           "opening,rs1,sending,rs1,loadstart,rs2,rs4,load,loadend",
+           "opening,rs1,sending,loadstart,rs2,rs4,load,loadend",
            "wrong responseText in test for " + test.toSource());
       }
       if (test.responseHeaders) {
         for (header in test.responseHeaders) {
           if (test.expectedResponseHeaders.indexOf(header) == -1) {
             is(res.responseHeaders[header], null,
                "wrong response header (" + header + ") in test for " +
                test.toSource());
@@ -684,17 +690,17 @@ function runTest() {
       is(res.status, 0, "wrong status in test for " + test.toSource());
       is(res.statusText, "", "wrong status text for " + test.toSource());
       is(res.responseXML, null,
          "wrong responseXML in test for " + test.toSource());
       is(res.responseText, "",
          "wrong responseText in test for " + test.toSource());
       if (!res.sendThrew) {
         is(res.events.join(","),
-           "opening,rs1,sending,rs1,loadstart,rs2,rs4,error,loadend",
+           "opening,rs1,sending,loadstart,rs2,rs4,error,loadend",
            "wrong events in test for " + test.toSource());
       }
       is(res.progressEvents, 0,
          "wrong events in test for " + test.toSource());
       if (test.responseHeaders) {
         for (header in test.responseHeaders) {
           is(res.responseHeaders[header], null,
              "wrong response header (" + header + ") in test for " +
@@ -812,30 +818,30 @@ function runTest() {
         "shouldn't have failed in test for " + test.toSource());
       is(res.status, 200, "wrong status in test for " + test.toSource());
       is(res.statusText, "OK", "wrong status text for " + test.toSource());
       is(res.responseXML, "<res>hello pass</res>",
          "wrong responseXML in test for " + test.toSource());
       is(res.responseText, "<res>hello pass</res>\n",
          "wrong responseText in test for " + test.toSource());
       is(res.events.join(","),
-         "opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load,loadend",
+         "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
          "wrong responseText in test for " + test.toSource());
     }
     else {
       is(res.didFail, true,
         "should have failed in test for " + test.toSource());
       is(res.status, 0, "wrong status in test for " + test.toSource());
       is(res.statusText, "", "wrong status text for " + test.toSource());
       is(res.responseXML, null,
          "wrong responseXML in test for " + test.toSource());
       is(res.responseText, "",
          "wrong responseText in test for " + test.toSource());
       is(res.events.join(","),
-         "opening,rs1,sending,rs1,loadstart,rs2,rs4,error,loadend",
+         "opening,rs1,sending,loadstart,rs2,rs4,error,loadend",
          "wrong events in test for " + test.toSource());
       is(res.progressEvents, 0,
          "wrong events in test for " + test.toSource());
     }
   }
 
   // Make sure to clear cookies to avoid affecting other tests
   document.cookie = "a=; path=/; expires=Thu, 01-Jan-1970 00:00:01 GMT"
@@ -1086,30 +1092,30 @@ function runTest() {
         "shouldn't have failed in test for " + test.toSource());
       is(res.status, 200, "wrong status in test for " + test.toSource());
       is(res.statusText, "OK", "wrong status text for " + test.toSource());
       is(res.responseXML, "<res>hello pass</res>",
          "wrong responseXML in test for " + test.toSource());
       is(res.responseText, "<res>hello pass</res>\n",
          "wrong responseText in test for " + test.toSource());
       is(res.events.join(","),
-         "opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load,loadend",
+         "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
          "wrong responseText in test for " + test.toSource());
     }
     else {
       is(res.didFail, true,
         "should have failed in test for " + test.toSource());
       is(res.status, 0, "wrong status in test for " + test.toSource());
       is(res.statusText, "", "wrong status text for " + test.toSource());
       is(res.responseXML, null,
          "wrong responseXML in test for " + test.toSource());
       is(res.responseText, "",
          "wrong responseText in test for " + test.toSource());
       is(res.events.join(","),
-         "opening,rs1,sending,rs1,loadstart,rs2,rs4,error,loadend",
+         "opening,rs1,sending,loadstart,rs2,rs4,error,loadend",
          "wrong events in test for " + test.toSource());
       is(res.progressEvents, 0,
          "wrong progressevents in test for " + test.toSource());
     }
   }
 
 
   SimpleTest.finish();
--- a/content/base/test/test_CrossSiteXHR_cache.html
+++ b/content/base/test/test_CrossSiteXHR_cache.html
@@ -467,29 +467,29 @@ function runTest() {
       is(res.didFail, false,
         "shouldn't have failed in test for " + testName);
       is(res.status, 200, "wrong status in test for " + testName);
       is(res.responseXML, "<res>hello pass</res>",
          "wrong responseXML in test for " + testName);
       is(res.responseText, "<res>hello pass</res>\n",
          "wrong responseText in test for " + testName);
       is(res.events.join(","),
-         "opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load,loadend",
+         "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
          "wrong events in test for " + testName);
     }
     else {
       is(res.didFail, true,
         "should have failed in test for " + testName);
       is(res.status, 0, "wrong status in test for " + testName);
       is(res.responseXML, null,
          "wrong responseXML in test for " + testName);
       is(res.responseText, "",
          "wrong responseText in test for " + testName);
       is(res.events.join(","),
-         "opening,rs1,sending,rs1,loadstart,rs2,rs4,error,loadend",
+         "opening,rs1,sending,loadstart,rs2,rs4,error,loadend",
          "wrong events in test for " + testName);
       is(res.progressEvents, 0,
          "wrong events in test for " + testName);
     }
   }
 
   SimpleTest.finish();
 
--- a/content/base/test/test_CrossSiteXHR_origin.html
+++ b/content/base/test/test_CrossSiteXHR_origin.html
@@ -127,17 +127,17 @@ function runTest() {
       is(res.status, 200, "wrong status for " + allowOrigin);
       is(res.statusText, "OK", "wrong status text for " + allowOrigin);
       is(res.responseXML,
          "<res>hello pass</res>",
          "wrong responseXML in test for " + allowOrigin);
       is(res.responseText, "<res>hello pass</res>\n",
          "wrong responseText in test for " + allowOrigin);
       is(res.events.join(","),
-         "opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load,loadend",
+         "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
          "wrong responseText in test for " + allowOrigin);
     }
 
     for each(allowOrigin in failTests) {
       req = {
         url: baseURL + "allowOrigin=" + escape(allowOrigin),
         method: "GET",
       };
@@ -145,17 +145,17 @@ function runTest() {
 
       res = eval(yield);
       is(res.didFail, true, "should have failed for " + allowOrigin);
       is(res.responseText, "", "should have no text for " + allowOrigin);
       is(res.status, 0, "should have no status for " + allowOrigin);
       is(res.statusText, "", "wrong status text for " + allowOrigin);
       is(res.responseXML, null, "should have no XML for " + allowOrigin);
       is(res.events.join(","),
-         "opening,rs1,sending,rs1,loadstart,rs2,rs4,error,loadend",
+         "opening,rs1,sending,loadstart,rs2,rs4,error,loadend",
          "wrong events in test for " + allowOrigin);
       is(res.progressEvents, 0,
          "wrong events in test for " + allowOrigin);
     }
   }
 
   SimpleTest.finish();
 
--- a/content/base/test/test_bug218236.html
+++ b/content/base/test/test_bug218236.html
@@ -31,17 +31,17 @@ var url_multipart = "file_bug218236_mult
 // List of tests: name of the test, URL to be requested, expected sequence
 // of events and optionally a function to be called from readystatechange handler.
 // Numbers in the list of events are values of XMLHttpRequest.readyState
 // when readystatechange event is triggered.
 var tests = [
   ["200 OK",                            url_200,                [1, 2, 3, 4, "load"], 0, null],
   ["404 Not Found",                     url_404,                [1, 2, 3, 4, "load"], 0, null],
   ["connection error",                  url_connection_error,   [1, 2, 4, "error"],   0, null],
-  ["abort() call on readyState = 1",    url_200,                [1, 4],               0, doAbort1],
+  ["abort() call on readyState = 1",    url_200,                [1, 4],               0, null, doAbort1],
   ["abort() call on readyState = 2",    url_200,                [1, 2, 4],            0, doAbort2],
   ["multipart document",                url_multipart,          [1, 2, 3, 4, "load",
                                                                  1, 2, 3, 4, "load",
                                                                  1, 2, 3, 4, "load"], 1, null],
 ];
 
 var testName = null;
 var currentState = 0;
@@ -66,54 +66,54 @@ function doAbort2() {
 /* Utility functions */
 
 function runNextTest() {
   if (tests.length > 0) {
     var test = tests.shift();
 
     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
-    // Prepare request object
-    request = new XMLHttpRequest();
-    request.multipart = test[3];
-    request.open("GET", test[1]);
-    request.onreadystatechange = onReadyStateChange;
-    request.onload = onLoad;
-    request.onerror = onError;
-
     // Initialize state variables
     testName = test[0]
     currentState = 0;
     currentSequence = [];
     expectedSequence = test[2];
     currentCallback = test[4];
+    postSendCallback = test[5];
+
+    // Prepare request object
+    request = new XMLHttpRequest();
+    request.multipart = test[3];
+    request.onreadystatechange = onReadyStateChange;
+    request.open("GET", test[1]);
+    request.onload = onLoad;
+    request.onerror = onError;
 
     // Start request
     request.send(null);
+    if (postSendCallback)
+      postSendCallback();
   }
   else
     SimpleTest.finish();
 }
 
 function finalizeTest() {
   finalizeTimeoutID = null;
   ok(compareArrays(expectedSequence, currentSequence), "event sequence for '" + testName + "' was " + currentSequence.join(", "));
 
   runNextTest();
 }
 
 function onReadyStateChange() {
   clearTimeout(finalizeTimeoutID);
   finalizeTimeoutID = null;
 
-  // Ignore duplicated calls for the same ready state
-  if (request.readyState != currentState) {
-    currentState = request.readyState;
-    currentSequence.push(currentState);
-  }
+  currentState = request.readyState;
+  currentSequence.push(currentState);
 
   if (currentState == 4) {
     // Allow remaining event to fire but then we are finished with this test
     // unless we get another onReadyStateChange in which case we'll cancel
     // this timeout
     finalizeTimeoutID = setTimeout(finalizeTest, 0);
   }
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug813919.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=813919
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 813919</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=813919">Mozilla Bug 813919</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 813919 **/
+
+  function testDataNode(dataNode) {
+    var div = document.createElement("div");
+    div.appendChild(dataNode);
+    var span = document.createElement("span");
+    div.appendChild(span);
+    var r = document.createRange();
+    r.setStart(dataNode, 0);
+    r.setEnd(div, div.childNodes.length);
+    r.deleteContents();
+    ok(r.collapsed, "Range should be collapsed!");
+    is(r.startContainer, div, "startContainer should be div.");
+    is(r.startOffset, div.childNodes.length,
+       "Range should be collaped to the end of the div element.");
+  }
+
+  testDataNode(document.createProcessingInstruction("x", "x"));
+  testDataNode(document.createComment("x"));
+  testDataNode(document.createTextNode("x"));
+
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_xhr_send_readystate.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=814064
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 814064</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=814064">Mozilla Bug 814064</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 814064 **/
+var xhr = new XMLHttpRequest();
+var firings = 0;
+function readyStateHandler() {
+  is(xhr.readyState, XMLHttpRequest.OPENED, "Should be in OPENED state");
+  ++firings;
+}
+xhr.onreadystatechange = readyStateHandler;
+xhr.open("GET", "");
+is(firings, 1, "Should have fired the readystatechange handler");
+xhr.send();
+is(firings, 1, "Should not have fired the readystatechange handler");
+xhr.onreadystatechange = null;
+xhr.abort();
+is(firings, 1, "Should not have fired the handler no-longer-registered handler");
+</script>
+</pre>
+</body>
+</html>
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -1893,16 +1893,17 @@ CanvasRenderingContext2D::EnsureWritable
     NS_ASSERTION(!mPathTransformWillUpdate, "mPathTransformWillUpdate should be false, if all paths are null");
     mPathBuilder = mTarget->CreatePathBuilder(fillRule);
   } else if (!mPathTransformWillUpdate) {
     mPathBuilder = mPath->CopyToBuilder(fillRule);
   } else {
     mDSPathBuilder =
       mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
     mPathTransformWillUpdate = false;
+    mPath = nullptr;
   }
 }
 
 void
 CanvasRenderingContext2D::EnsureUserSpacePath(bool aCommitTransform /* = true */)
 {
   FillRule fillRule = CurrentState().fillRule;
 
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -138,17 +138,18 @@ public:
   CanvasRenderingContext2D();
   virtual ~CanvasRenderingContext2D();
 
   virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
                                bool *triedToWrap);
 
   nsHTMLCanvasElement* GetCanvas() const
   {
-    return mCanvasElement;
+    // corresponds to changes to the old bindings made in bug 745025
+    return mCanvasElement->GetOriginalCanvas();
   }
 
   void Save();
   void Restore();
   void Scale(double x, double y, mozilla::ErrorResult& error);
   void Rotate(double angle, mozilla::ErrorResult& error);
   void Translate(double x, double y, mozilla::ErrorResult& error);
   void Transform(double m11, double m12, double m21, double m22, double dx,
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -3151,17 +3151,16 @@ WebGLContext::ReadPixels(WebGLint x, Web
 
     if (!pixels)
         return ErrorInvalidValue("readPixels: null destination buffer");
 
     const WebGLRectangleObject *framebufferRect = FramebufferRectangleObject();
     WebGLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
     WebGLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
 
-    void* data = pixels->Data();
     uint32_t dataByteLen = JS_GetTypedArrayByteLength(pixels->Obj());
     int dataType = JS_GetTypedArrayType(pixels->Obj());
 
     uint32_t channels = 0;
 
     // Check the format param
     switch (format) {
         case LOCAL_GL_ALPHA:
@@ -3210,16 +3209,22 @@ WebGLContext::ReadPixels(WebGLint x, Web
         RoundedToNextMultipleOf(checked_plainRowSize, mPixelStorePackAlignment);
 
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("readPixels: integer overflow computing the needed buffer size");
 
     if (checked_neededByteLength.value() > dataByteLen)
         return ErrorInvalidOperation("readPixels: buffer too small");
 
+    void* data = pixels->Data();
+    if (!data) {
+        ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?");
+        return rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    }
+
     // Check the format and type params to assure they are an acceptable pair (as per spec)
     switch (format) {
         case LOCAL_GL_RGBA: {
             switch (type) {
                 case LOCAL_GL_UNSIGNED_BYTE:
                     break;
                 default:
                     return ErrorInvalidOperation("readPixels: Invalid format/type pair");
--- a/content/canvas/src/WebGLElementArrayCache.cpp
+++ b/content/canvas/src/WebGLElementArrayCache.cpp
@@ -301,17 +301,17 @@ public:
   }
 
   void Invalidate(size_t firstByte, size_t lastByte);
 
   void Update();
 
   size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
   {
-    return aMallocSizeOf(this) + aMallocSizeOf(mTreeData.Elements());
+    return aMallocSizeOf(this) + mTreeData.SizeOfExcludingThis(aMallocSizeOf);
   }
 };
 
 // TreeForType: just a template helper to select the right tree object for a given
 // element type.
 template<typename T>
 struct TreeForType {};
 
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/skipped_tests_android.txt
@@ -0,0 +1,5 @@
+conformance/more/conformance/quickCheckAPI-B2.html
+conformance/programs/gl-getshadersource.html
+conformance/reading/read-pixels-test.html
+conformance/textures/gl-teximage.html
+conformance/textures/tex-image-and-sub-image-2d-with-image.html
--- a/content/canvas/test/webgl/test_webgl_conformance_test_suite.html
+++ b/content/canvas/test/webgl/test_webgl_conformance_test_suite.html
@@ -255,16 +255,20 @@ function start() {
   Reporter.prototype.addPage = function(url) {
     this.currentPage = new Page(this, url, this.resultElem);
     this.resultElem.appendChild(this.currentPage.elem);
     ++this.totalPages;
     this.pagesByURL[url] = this.currentPage;
   };
 
   Reporter.prototype.startPage = function(url) {
+    if (testsToSkip.indexOf(url) != -1) {
+      info("[" + url + "] (WebGL mochitest) Skipping test page");
+      return false;
+    }
     info("[" + url + "] (WebGL mochitest) Starting test page");
 
     // Calling garbageCollect before each test page fixes intermittent failures with
     // out-of-memory errors, often failing to create a WebGL context.
     // The explanation is that the JS engine keeps unreferenced WebGL contexts around
     // for too long before GCing (bug 617453), so that during this mochitest dozens of unreferenced
     // WebGL contexts can accumulate at a given time.
     SpecialPowers.DOMWindowUtils.garbageCollect();
@@ -432,16 +436,24 @@ function start() {
     indexOfEmptyFilename = testsExpectedToFail.indexOf("");
   }
 
   if (kIsWindows && !kIsWindowsVistaOrHigher)
     testsExpectedToFail.push('conformance/textures/texture-mips.html');
 
   var testsToIgnore = [];
 
+  var testsToSkip = [];
+
+  if (kIsAndroid) {
+    var testsToSkip = loadTextFileSynchronous('skipped_tests_android.txt')
+                      .replace(/\r/g, '') // convert to unix line breaks
+                      .split('\n');
+  }
+
   runTestSuite();
 }
 
 </script>
 </head>
 <body onload="start();">
 <p id="display"></p>
 <div id="content" style="display: none">
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -145,25 +145,25 @@ nsEventListenerManager::Shutdown()
   nsDOMEvent::Shutdown();
 }
 
 NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(nsEventListenerManager)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsEventListenerManager, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsEventListenerManager, Release)
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsEventListenerManager)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsEventListenerManager)
   uint32_t count = tmp->mListeners.Length();
   for (uint32_t i = 0; i < count; i++) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mListeners[i] mListener");
     cb.NoteXPCOMChild(tmp->mListeners.ElementAt(i).mListener.get());
   }  
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsEventListenerManager)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsEventListenerManager)
   tmp->Disconnect();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 nsPIDOMWindow*
 nsEventListenerManager::GetInnerWindowForTarget()
 {
   nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
@@ -599,17 +599,21 @@ nsEventListenerManager::SetEventHandler(
 
   nsCOMPtr<nsIScriptGlobalObject> global;
 
   if (node) {
     // Try to get context from doc
     // XXX sXBL/XBL2 issue -- do we really want the owner here?  What
     // if that's the XBL document?
     doc = node->OwnerDoc();
-    global = doc->GetScriptGlobalObject();
+    MOZ_ASSERT(!doc->IsLoadedAsData(), "Should not get in here at all");
+
+    // We want to allow compiling an event handler even in an unloaded
+    // document, so use GetScopeObject here, not GetScriptHandlingObject.
+    global = doc->GetScopeObject();
   } else {
     nsCOMPtr<nsPIDOMWindow> win = GetTargetAsInnerWindow();
     if (win) {
       nsCOMPtr<nsIDOMDocument> domdoc;
       win->GetDocument(getter_AddRefs(domdoc));
       doc = do_QueryInterface(domdoc);
       global = do_QueryInterface(win);
     } else {
@@ -618,16 +622,23 @@ nsEventListenerManager::SetEventHandler(
   }
 
   if (!global) {
     // This can happen; for example this document might have been
     // loaded as data.
     return NS_OK;
   }
 
+#ifdef DEBUG
+  nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(global);
+  if (win) {
+    MOZ_ASSERT(win->IsInnerWindow(), "We should not have an outer window here!");
+  }
+#endif
+
   nsresult rv = NS_OK;
   // return early preventing the event listener from being added
   // 'doc' is fetched above
   if (doc) {
     // Don't allow adding an event listener if the document is sandboxed
     // without 'allow-scripts'.
     if (doc->GetSandboxFlags() & SANDBOXED_SCRIPTS) {
       return NS_ERROR_DOM_SECURITY_ERR;
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -158,38 +158,36 @@ public:
   static void StopHandlingUserInput()
   {
     --sUserInputEventDepth;
     if (sUserInputEventDepth == 0) {
       sHandlingInputStart = TimeStamp();
     }
   }
 
+  /**
+   * Returns true if the current code is being executed as a result of user input.
+   * This includes timers or anything else that is initiated from user input.
+   * However, mouse over events are not counted as user input, nor are
+   * page load events. If this method is called from asynchronously executed code,
+   * such as during layout reflows, it will return false. If more time has elapsed
+   * since the user input than is specified by the
+   * dom.event.handling-user-input-time-limit pref (default 1 second), this
+   * function also returns false.
+   */
   static bool IsHandlingUserInput()
   {
     if (sUserInputEventDepth <= 0) {
       return false;
     }
     TimeDuration timeout = nsContentUtils::HandlingUserInputTimeout();
     return timeout <= TimeDuration(0) ||
            (TimeStamp::Now() - sHandlingInputStart) <= timeout;
   }
 
-  /**
-   * Returns true if the current code is being executed as a result of user input.
-   * This includes timers or anything else that is initiated from user input.
-   * However, mouse hover events are not counted as user input, nor are
-   * page load events. If this method is called from asynchronously executed code,
-   * such as during layout reflows, it will return false. If more time has elapsed
-   * since the user input than is specified by the
-   * dom.event.handling-user-input-time-limit pref (default 1 second), this
-   * function also returns false.
-   */
-  NS_IMETHOD_(bool) IsHandlingUserInputExternal() { return IsHandlingUserInput(); }
-  
   nsPresContext* GetPresContext() { return mPresContext; }
 
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsEventStateManager,
                                            nsIObserver)
 
   static nsIDocument* sMouseOverDocument;
 
   static nsEventStateManager* GetActiveEventStateManager() { return sActiveESM; }
--- a/content/events/test/Makefile.in
+++ b/content/events/test/Makefile.in
@@ -94,16 +94,17 @@ MOCHITEST_FILES = \
 		test_continuous_wheel_events.html \
 		test_moz_mouse_pixel_scroll_event.html \
 		test_wheel_default_action.html \
 		window_wheel_default_action.html \
 		test_bug603008.html \
 		test_bug716822.html \
 		test_bug742376.html \
 		test_dragstart.html \
+		test_bug812744.html \
 		$(NULL)
 
 MOCHITEST_CHROME_FILES = \
 		test_bug336682_2.xul \
 		test_bug336682.js \
 		test_bug586961.xul \
 		test_bug415498.xul \
 		bug415498-doc1.html \
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_bug812744.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=812744
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 812744</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=812744">Mozilla Bug 812744</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="f"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 812744 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+  var f = $("f");
+  var el = f.contentDocument.documentElement;
+  f.onload = function() {
+    el.setAttribute("onmouseleave", "(void 0)");
+    is(el.onmouseleave.toString(), "function onmouseleave(event) {\n(void 0)\n}",
+       "Should have a function here");
+    SimpleTest.finish();
+  };
+  f.src = "http://www.example.com/"
+});
+</script>
+</pre>
+</body>
+</html>
--- a/content/html/content/public/nsHTMLCanvasElement.h
+++ b/content/html/content/public/nsHTMLCanvasElement.h
@@ -168,17 +168,17 @@ protected:
   nsresult MozGetAsFileImpl(const nsAString& aName,
                             const nsAString& aType,
                             nsIDOMFile** aResult);
   nsresult GetContextHelper(const nsAString& aContextId,
                             nsICanvasRenderingContextInternal **aContext);
   void CallPrintCallback();
 
   nsString mCurrentContextId;
-  nsCOMPtr<nsIDOMHTMLCanvasElement> mOriginalCanvas;
+  nsRefPtr<nsHTMLCanvasElement> mOriginalCanvas;
   nsCOMPtr<nsIPrintCallback> mPrintCallback;
   nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
   nsCOMPtr<nsHTMLCanvasPrintState> mPrintState;
   
 public:
   // Record whether this canvas should be write-only or not.
   // We set this when script paints an image from a different origin.
   // We also transitively set it when script paints a canvas which
@@ -188,17 +188,17 @@ public:
   bool IsPrintCallbackDone();
 
   void HandlePrintCallback(nsPresContext::nsPresContextType aType);
 
   nsresult DispatchPrintCallback(nsITimerCallback* aCallback);
 
   void ResetPrintCallback();
 
-  nsIDOMHTMLCanvasElement* GetOriginalCanvas();
+  nsHTMLCanvasElement* GetOriginalCanvas();
 };
 
 inline nsISupports*
 GetISupports(nsHTMLCanvasElement* p)
 {
   return static_cast<mozilla::dom::Element*>(p);
 }
 
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -19,16 +19,17 @@
 #include "AudioStream.h"
 #include "VideoFrameContainer.h"
 #include "mozilla/CORSMode.h"
 #include "nsDOMMediaStream.h"
 #include "mozilla/Mutex.h"
 #include "nsTimeRanges.h"
 #include "nsIDOMWakeLock.h"
 #include "AudioChannelCommon.h"
+#include "DecoderTraits.h"
 
 // Define to output information on decoding and painting framerate
 /* #define DEBUG_FRAME_RATE 1 */
 
 typedef uint16_t nsMediaNetworkState;
 typedef uint16_t nsMediaReadyState;
 
 namespace mozilla {
@@ -53,22 +54,16 @@ public:
   typedef mozilla::MetadataTags MetadataTags;
   typedef mozilla::AudioStream AudioStream;
   typedef mozilla::MediaDecoder MediaDecoder;
 
 #ifdef MOZ_DASH
   friend class DASHDecoder;
 #endif
 
-  enum CanPlayStatus {
-    CANPLAY_NO,
-    CANPLAY_MAYBE,
-    CANPLAY_YES
-  };
-
   mozilla::CORSMode GetCORSMode() {
     return mCORSMode;
   }
 
   nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo);
   virtual ~nsHTMLMediaElement();
 
   /**
@@ -249,76 +244,19 @@ public:
 
   // called to notify that the principal of the decoder's media resource has changed.
   virtual void NotifyDecoderPrincipalChanged() MOZ_FINAL MOZ_OVERRIDE;
 
   // Update the visual size of the media. Called from the decoder on the
   // main thread when/if the size changes.
   void UpdateMediaSize(nsIntSize size);
 
-  // Returns the CanPlayStatus indicating if we can handle this
-  // MIME type. The MIME type should not include the codecs parameter.
-  // If it returns anything other than CANPLAY_NO then it also
-  // returns a null-terminated list of supported codecs
-  // in *aSupportedCodecs. This list should not be freed, it is static data.
-  static CanPlayStatus CanHandleMediaType(const char* aMIMEType,
-                                          char const *const ** aSupportedCodecs);
-
   // Returns the CanPlayStatus indicating if we can handle the
   // full MIME type including the optional codecs parameter.
-  static CanPlayStatus GetCanPlay(const nsAString& aType);
-
-  // Returns true if we should handle this MIME type when it appears
-  // as an <object> or as a toplevel page. If, in practice, our support
-  // for the type is more limited than appears in the wild, we should return
-  // false here even if CanHandleMediaType would return true.
-  static bool ShouldHandleMediaType(const char* aMIMEType);
-
-#ifdef MOZ_OGG
-  static bool IsOggType(const nsACString& aType);
-  static const char gOggTypes[3][16];
-  static char const *const gOggCodecs[3];
-  static char const *const gOggCodecsWithOpus[4];
-#endif
-
-#ifdef MOZ_WAVE
-  static bool IsWaveType(const nsACString& aType);
-  static const char gWaveTypes[4][15];
-  static char const *const gWaveCodecs[2];
-#endif
-
-#ifdef MOZ_WEBM
-  static bool IsWebMType(const nsACString& aType);
-  static const char gWebMTypes[2][11];
-  static char const *const gWebMCodecs[4];
-#endif
-
-#ifdef MOZ_GSTREAMER
-  static bool IsGStreamerSupportedType(const nsACString& aType);
-  static bool IsH264Type(const nsACString& aType);
-  static const char gH264Types[3][16];
-#endif
-
-#ifdef MOZ_WIDGET_GONK
-  static bool IsOmxSupportedType(const nsACString& aType);
-  static const char gOmxTypes[5][16];
-#endif
-
-#if defined(MOZ_GSTREAMER) || defined(MOZ_WIDGET_GONK)
-  static char const *const gH264Codecs[9];
-#endif
-
-#ifdef MOZ_MEDIA_PLUGINS
-  static bool IsMediaPluginsType(const nsACString& aType);
-#endif
-
-#ifdef MOZ_DASH
-  static bool IsDASHMPDType(const nsACString& aType);
-  static const char gDASHMPDTypes[1][21];
-#endif
+  static mozilla::CanPlayStatus GetCanPlay(const nsAString& aType);
 
   /**
    * Get the mime type for this element.
    */
   void GetMimeType(nsCString& aMimeType);
 
   /**
    * Called when a child source element is added to this media element. This
@@ -641,16 +579,21 @@ protected:
    **/
   void GetCurrentSpec(nsCString& aString);
 
   /**
    * Process any media fragment entries in the URI
    */
   void ProcessMediaFragmentURI();
 
+  /**
+   * Mute or unmute the audio, without changing the value that |muted| reports.
+   */
+  void SetMutedInternal(bool aMuted);
+
   // Get the nsHTMLMediaElement object if the decoder is being used from an
   // HTML media element, and null otherwise.
   virtual nsHTMLMediaElement* GetMediaElement() MOZ_FINAL MOZ_OVERRIDE
   {
     return this;
   }
 
   // Return true if decoding should be paused
@@ -784,24 +727,38 @@ protected:
   // fragment time has been set. Read/Write from the main thread only.
   double mFragmentStart;
 
   // Logical end time of the media resource in seconds as obtained
   // from any media fragments. A negative value indicates that no
   // fragment time has been set. Read/Write from the main thread only.
   double mFragmentEnd;
 
+  // The defaultPlaybackRate attribute gives the desired speed at which the
+  // media resource is to play, as a multiple of its intrinsic speed.
+  double mDefaultPlaybackRate;
+
+  // The playbackRate attribute gives the speed at which the media resource
+  // plays, as a multiple of its intrinsic speed. If it is not equal to the
+  // defaultPlaybackRate, then the implication is that the user is using a
+  // feature such as fast forward or slow motion playback.
+  double mPlaybackRate;
+
+  // True if pitch correction is applied when playbackRate is set to a
+  // non-intrinsic value.
+  bool mPreservesPitch;
+
   nsRefPtr<gfxASurface> mPrintSurface;
 
   // Reference to the source element last returned by GetNextSource().
   // This is the child source element which we're trying to load from.
   nsCOMPtr<nsIContent> mSourceLoadCandidate;
 
   // An audio stream for writing audio directly from JS.
-  nsRefPtr<AudioStream> mAudioStream;
+  nsAutoPtr<AudioStream> mAudioStream;
 
   // Range of time played.
   nsTimeRanges mPlayed;
 
   // Stores the time at the start of the current 'played' range.
   double mCurrentPlayRangeStart;
 
   // True if MozAudioAvailable events can be safely dispatched, based on
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -63,16 +63,17 @@
 #include "nsIDOMEvent.h"
 #include "nsDOMCSSDeclaration.h"
 #include "nsITextControlFrame.h"
 #include "nsIForm.h"
 #include "nsIFormControl.h"
 #include "nsIDOMHTMLFormElement.h"
 #include "nsHTMLFormElement.h"
 #include "nsFocusManager.h"
+#include "nsAttrValueOrString.h"
 
 #include "nsMutationEvent.h"
 
 #include "nsContentCID.h"
 
 #include "nsDOMStringMap.h"
 
 #include "nsIEditor.h"
@@ -97,17 +98,16 @@
 #include "nsThreadUtils.h"
 #include "nsTextFragment.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/ErrorResult.h"
 #include "nsHTMLDocument.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
-using namespace mozilla::directionality;
 
 class nsINodeInfo;
 class nsIDOMNodeList;
 class nsRuleWalker;
 
 // XXX todo: add in missing out-of-memory checks
 
 //----------------------------------------------------------------------
@@ -342,18 +342,19 @@ nsGenericHTMLElement::ClearDataset()
   NS_ASSERTION(slots && slots->mDataset,
                "Slots should exist and dataset should not be null.");
   slots->mDataset = nullptr;
 
   return NS_OK;
 }
 
 static const nsAttrValue::EnumTable kDirTable[] = {
-  { "ltr", NS_STYLE_DIRECTION_LTR },
-  { "rtl", NS_STYLE_DIRECTION_RTL },
+  { "ltr", eDir_LTR },
+  { "rtl", eDir_RTL },
+  { "auto", eDir_Auto },
   { 0 }
 };
 
 void
 nsGenericHTMLElement::GetAccessKeyLabel(nsAString& aLabel)
 {
   nsPresContext *presContext = GetPresContext();
 
@@ -1694,16 +1695,33 @@ nsGenericHTMLElement::GetHrefURIForAncho
   // We use the nsAttrValue's copy of the URI string to avoid copying.
   nsCOMPtr<nsIURI> uri;
   GetURIAttr(nsGkAtoms::href, nullptr, getter_AddRefs(uri));
 
   return uri.forget();
 }
 
 nsresult
+nsGenericHTMLElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
+                                    const nsAttrValueOrString* aValue,
+                                    bool aNotify)
+{
+  if (aNamespaceID == kNameSpaceID_None &&
+      aName == nsGkAtoms::dir &&
+      HasDirAuto()) {
+      // setting dir on an element that currently has dir=auto
+    WalkDescendantsClearAncestorDirAuto(this);
+    SetHasDirAuto();
+  }
+
+  return nsGenericHTMLElementBase::BeforeSetAttr(aNamespaceID, aName,
+                                                 aValue, aNotify);
+}
+
+nsresult
 nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
                                    const nsAttrValue* aValue, bool aNotify)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     uint32_t eventType = EventNameType_HTML;
     if (mNodeInfo->Equals(nsGkAtoms::body) ||
         mNodeInfo->Equals(nsGkAtoms::frameset)) {
       eventType |= EventNameType_HTMLBodyOrFramesetOnly;
@@ -1714,26 +1732,38 @@ nsGenericHTMLElement::AfterSetAttr(int32
         "Expected string value for script body");
       nsresult rv = SetEventHandler(aName, aValue->GetStringValue());
       NS_ENSURE_SUCCESS(rv, rv);
     }
     else if (aNotify && aName == nsGkAtoms::spellcheck) {
       SyncEditorsOnSubtree(this);
     }
     else if (aName == nsGkAtoms::dir) {
-      Directionality dir;
-      if (aValue &&
-          (aValue->Equals(nsGkAtoms::ltr, eIgnoreCase) ||
-           aValue->Equals(nsGkAtoms::rtl, eIgnoreCase))) {
+      Directionality dir = eDir_LTR;
+      if (aValue && aValue->Type() == nsAttrValue::eEnum) {
         SetHasValidDir();
-        dir = aValue->Equals(nsGkAtoms::rtl, eIgnoreCase) ? eDir_RTL : eDir_LTR;
-        SetDirectionality(dir, aNotify);
+        Directionality dirValue = (Directionality)aValue->GetEnumValue();
+        if (dirValue == eDir_Auto) {
+          SetHasDirAuto();
+          ClearHasFixedDir();
+        } else {
+          dir = dirValue;
+          SetDirectionality(dir, aNotify);
+          ClearHasDirAuto();
+          ClearHasDirAutoSet();
+          SetHasFixedDir();
+        }
       } else {
         ClearHasValidDir();
-        dir = RecomputeDirectionality(this, aNotify);
+        ClearHasFixedDir();
+        if (NodeInfo()->Equals(nsGkAtoms::bdi)) {
+          SetHasDirAuto();
+        } else {
+          dir = RecomputeDirectionality(this, aNotify);
+        }
       }
       SetDirectionalityOnDescendants(this, dir, aNotify);
     }
   }
 
   return nsGenericHTMLElementBase::AfterSetAttr(aNamespaceID, aName,
                                                 aValue, aNotify);
 }
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -772,16 +772,20 @@ private:
 protected:
   /**
    * Determine whether an attribute is an event (onclick, etc.)
    * @param aName the attribute
    * @return whether the name is an event handler name
    */
   bool IsEventName(nsIAtom* aName);
 
+  virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
+                                 const nsAttrValueOrString* aValue,
+                                 bool aNotify);
+
   virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue, bool aNotify);
 
   virtual nsEventListenerManager*
     GetEventListenerManagerForAttr(nsIAtom* aAttrName, bool* aDefer);
 
   virtual const nsAttrName* InternalGetExistingAttrNameFromQName(const nsAString& aStr) const;
 
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -302,42 +302,42 @@ nsHTMLCanvasElement::IsPrintCallbackDone
 {
   if (mPrintState == nullptr) {
     return true;
   }
 
   return mPrintState->mIsDone;
 }
 
-nsIDOMHTMLCanvasElement*
+nsHTMLCanvasElement*
 nsHTMLCanvasElement::GetOriginalCanvas()
 {
   return mOriginalCanvas ? mOriginalCanvas.get() : this;
 }
 
 nsresult
 nsHTMLCanvasElement::CopyInnerTo(Element* aDest)
 {
   nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
   NS_ENSURE_SUCCESS(rv, rv);
   if (aDest->OwnerDoc()->IsStaticDocument()) {
     nsHTMLCanvasElement* dest = static_cast<nsHTMLCanvasElement*>(aDest);
     nsHTMLCanvasElement* self = const_cast<nsHTMLCanvasElement*>(this);
     dest->mOriginalCanvas = self;
 
-    HTMLImageOrCanvasOrVideoElement element;
-    element.SetAsHTMLCanvasElement() = this;
     nsCOMPtr<nsISupports> cxt;
     dest->GetContext(NS_LITERAL_STRING("2d"), JSVAL_VOID, getter_AddRefs(cxt));
     nsRefPtr<CanvasRenderingContext2D> context2d =
       static_cast<CanvasRenderingContext2D*>(cxt.get());
     if (context2d && !self->mPrintCallback) {
+      HTMLImageOrCanvasOrVideoElement element;
+      element.SetAsHTMLCanvasElement() = this;
       ErrorResult err;
       context2d->DrawImage(element,
-                           0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, err);
+                           0.0, 0.0, err);
       rv = err.ErrorCode();
     }
   }
   return rv;
 }
 
 nsChangeHint
 nsHTMLCanvasElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -76,16 +76,17 @@
 #include "nsGlobalWindow.h"
 
 // input type=image
 #include "nsImageLoadingContent.h"
 
 #include "mozAutoDocUpdate.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsContentUtils.h"
+#include "mozilla/dom/DirectionalityUtils.h"
 #include "nsRadioVisitor.h"
 
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Util.h" // DebugOnly
 #include "mozilla/Preferences.h"
 
 #include "nsIIDNService.h"
 
@@ -755,16 +756,20 @@ nsHTMLInputElement::BeforeSetAttr(int32_
       if (aValue) {
         LoadImage(aValue->String(), true, aNotify);
       } else {
         // Null value means the attr got unset; drop the image
         CancelImageRequests(aNotify);
       }
     } else if (aNotify && aName == nsGkAtoms::disabled) {
       mDisabledChanged = true;
+    } else if (aName == nsGkAtoms::dir &&
+               AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
+                           nsGkAtoms::_auto, eIgnoreCase)) {
+      SetDirectionIfAuto(false, aNotify);
     }
   }
 
   return nsGenericHTMLFormElement::BeforeSetAttr(aNameSpaceID, aName,
                                                  aValue, aNotify);
 }
 
 nsresult
@@ -861,16 +866,19 @@ nsHTMLInputElement::AfterSetAttr(int32_t
       UpdateHasRange();
       UpdateRangeOverflowValidityState();
     } else if (aName == nsGkAtoms::min) {
       UpdateHasRange();
       UpdateRangeUnderflowValidityState();
       UpdateStepMismatchValidityState();
     } else if (aName == nsGkAtoms::step) {
       UpdateStepMismatchValidityState();
+    } else if (aName == nsGkAtoms::dir &&
+               aValue && aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) {
+      SetDirectionIfAuto(true, aNotify);
     }
 
     UpdateState(aNotify);
   }
 
   return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName,
                                                 aValue, aNotify);
 }
@@ -2577,16 +2585,19 @@ nsHTMLInputElement::BindToTree(nsIDocume
   }
 
   // Add radio to document if we don't have a form already (if we do it's
   // already been added into that group)
   if (aDocument && !mForm && mType == NS_FORM_INPUT_RADIO) {
     AddedToRadioGroup();
   }
 
+  // Set direction based on value if dir=auto
+  SetDirectionIfAuto(HasDirAuto(), false);
+
   // An element can't suffer from value missing if it is not in a document.
   // We have to check if we suffer from that as we are now in a document.
   UpdateValueMissingValidityState();
 
   // If there is a disabled fieldset in the parent chain, the element is now
   // barred from constraint validation and can't suffer from value missing
   // (call done before).
   UpdateBarredFromConstraintValidation();
@@ -3192,16 +3203,31 @@ nsHTMLInputElement::SetDefaultValueAsVal
   // it's in the value mode value.
   nsAutoString resetVal;
   GetDefaultValue(resetVal);
 
   // SetValueInternal is going to sanitize the value.
   return SetValueInternal(resetVal, false, false);
 }
 
+void
+nsHTMLInputElement::SetDirectionIfAuto(bool aAuto, bool aNotify)
+{
+  if (aAuto) {
+    SetHasDirAuto();
+    if (IsSingleLineTextControl(true)) {
+      nsAutoString value;
+      GetValue(value);
+      SetDirectionalityFromValue(this, value, aNotify);
+    }
+  } else {
+    ClearHasDirAuto();
+  }
+}
+
 NS_IMETHODIMP
 nsHTMLInputElement::Reset()
 {
   // We should be able to reset all dirty flags regardless of the type.
   SetCheckedChanged(false);
   SetValueChanged(false);
 
   switch (GetValueMode()) {
@@ -4601,16 +4627,20 @@ nsHTMLInputElement::InitializeKeyboardEv
     state->InitializeKeyboardEventListeners();
   }
 }
 
 NS_IMETHODIMP_(void)
 nsHTMLInputElement::OnValueChanged(bool aNotify)
 {
   UpdateAllValidityStates(aNotify);
+
+  if (HasDirAuto()) {
+    SetDirectionIfAuto(true, aNotify);
+  }
 }
 
 NS_IMETHODIMP_(bool)
 nsHTMLInputElement::HasCachedSelection()
 {
   bool isCached = false;
   nsTextEditorState *state = GetEditorState();
   if (state) {
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -506,16 +506,18 @@ protected:
 
   /**
    * Set the current default value to the value of the input element.
    * @note You should not call this method if GetValueMode() doesn't return
    * VALUE_MODE_VALUE.
    */
   nsresult SetDefaultValueAsValue();
 
+  virtual void SetDirectionIfAuto(bool aAuto, bool aNotify);
+
   /**
    * Return if an element should have a specific validity UI
    * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
    *
    * @return Whether the elemnet should have a validity UI.
    */
   bool ShouldShowValidityUI() const {
     /**
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -36,17 +36,16 @@
 #include "jsapi.h"
 
 #include "nsITimer.h"
 
 #include "nsEventDispatcher.h"
 #include "nsMediaError.h"
 #include "MediaDecoder.h"
 #include "nsICategoryManager.h"
-#include "nsCharSeparatedTokenizer.h"
 #include "MediaResource.h"
 
 #include "nsIDOMHTMLVideoElement.h"
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsICachingChannel.h"
@@ -118,16 +117,28 @@ static PRLogModuleInfo* gMediaElementEve
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
 
 // Number of milliseconds between timeupdate events as defined by spec
 #define TIMEUPDATE_MS 250
 
+// These constants are arbitrary
+// Minimum playbackRate for a media
+static const double MIN_PLAYBACKRATE = 0.25;
+// Maximum playbackRate for a media
+static const double MAX_PLAYBACKRATE = 5.0;
+// These are the limits beyonds which SoundTouch does not perform too well and when
+// speech is hard to understand anyway.
+// Threshold above which audio is muted
+static const double THRESHOLD_HIGH_PLAYBACKRATE_AUDIO = 4.0;
+// Threshold under which audio is muted
+static const double THRESHOLD_LOW_PLAYBACKRATE_AUDIO = 0.5;
+
 // Under certain conditions there may be no-one holding references to
 // a media element from script, DOM parent, etc, but the element may still
 // fire meaningful events in the future so we can't destroy it yet:
 // 1) If the element is delaying the load event (or would be, if it were
 // in a document), then events up to loadeddata or error could be fired,
 // so we need to stay alive.
 // 2) If the element is not paused and playback has not ended, then
 // we will (or might) play, sending timeupdate and ended events and possibly
@@ -704,16 +715,17 @@ void nsHTMLMediaElement::QueueSelectReso
 /* void load (); */
 NS_IMETHODIMP nsHTMLMediaElement::Load()
 {
   if (mIsRunningLoadMethod)
     return NS_OK;
   SetPlayedOrSeeked(false);
   mIsRunningLoadMethod = true;
   AbortExistingLoads();
+  SetPlaybackRate(mDefaultPlaybackRate);
   QueueSelectResourceTask();
   mIsRunningLoadMethod = false;
   return NS_OK;
 }
 
 static bool HasSourceChildren(nsIContent *aElement)
 {
   for (nsIContent* child = aElement->GetFirstChild();
@@ -1180,16 +1192,17 @@ nsresult nsHTMLMediaElement::LoadWithCha
 
   ChangeDelayLoadStatus(true);
   rv = InitializeDecoderForChannel(aChannel, aListener);
   if (NS_FAILED(rv)) {
     ChangeDelayLoadStatus(false);
     return rv;
   }
 
+  SetPlaybackRate(mDefaultPlaybackRate);
   DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsHTMLMediaElement::MozLoadFrom(nsIDOMHTMLMediaElement* aOther)
 {
   NS_ENSURE_ARG_POINTER(aOther);
@@ -1210,16 +1223,17 @@ NS_IMETHODIMP nsHTMLMediaElement::MozLoa
 
   mLoadingSrc = other->mLoadingSrc;
   nsresult rv = InitializeDecoderAsClone(other->mDecoder);
   if (NS_FAILED(rv)) {
     ChangeDelayLoadStatus(false);
     return rv;
   }
 
+  SetPlaybackRate(mDefaultPlaybackRate);
   DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
 
   return NS_OK;
 }
 
 /* readonly attribute unsigned short readyState; */
 NS_IMETHODIMP nsHTMLMediaElement::GetReadyState(uint16_t *aReadyState)
 {
@@ -1527,31 +1541,35 @@ nsHTMLMediaElement::SetMozFrameBufferLen
 /* attribute boolean muted; */
 NS_IMETHODIMP nsHTMLMediaElement::GetMuted(bool *aMuted)
 {
   *aMuted = mMuted;
 
   return NS_OK;
 }
 
-NS_IMETHODIMP nsHTMLMediaElement::SetMuted(bool aMuted)
+void nsHTMLMediaElement::SetMutedInternal(bool aMuted)
 {
-  if (aMuted == mMuted)
-    return NS_OK;
-
-  mMuted = aMuted;
-
-  float effectiveVolume = mMuted ? 0.0f : float(mVolume);
+  float effectiveVolume = aMuted ? 0.0f : float(mVolume);
   if (mDecoder) {
     mDecoder->SetVolume(effectiveVolume);
   } else if (mAudioStream) {
     mAudioStream->SetVolume(effectiveVolume);
   } else if (mSrcStream) {
     GetSrcMediaStream()->SetAudioOutputVolume(this, effectiveVolume);
   }
+}
+
+NS_IMETHODIMP nsHTMLMediaElement::SetMuted(bool aMuted)
+{
+  if (aMuted == mMuted)
+    return NS_OK;
+
+  mMuted = aMuted;
+  SetMutedInternal(aMuted);
 
   DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
 
   return NS_OK;
 }
 
 already_AddRefed<nsDOMMediaStream>
 nsHTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded)
@@ -1708,16 +1726,19 @@ nsHTMLMediaElement::nsHTMLMediaElement(a
     mVolume(1.0),
     mChannels(0),
     mRate(0),
     mPreloadAction(PRELOAD_UNDEFINED),
     mMediaSize(-1,-1),
     mLastCurrentTime(0.0),
     mFragmentStart(-1.0),
     mFragmentEnd(-1.0),
+    mDefaultPlaybackRate(1.0),
+    mPlaybackRate(1.0),
+    mPreservesPitch(true),
     mCurrentPlayRangeStart(-1.0),
     mAllowAudioData(false),
     mBegun(false),
     mLoadedFirstFrame(false),
     mAutoplaying(true),
     mAutoplayEnabled(true),
     mPaused(true),
     mMuted(false),
@@ -1852,17 +1873,16 @@ NS_IMETHODIMP nsHTMLMediaElement::Play()
   }
 
   if (mCurrentPlayRangeStart == -1.0) {
     GetCurrentTime(&mCurrentPlayRangeStart);
   }
 
   // TODO: If the playback has ended, then the user agent must set
   // seek to the effective start.
-  // TODO: The playback rate must be set to the default playback rate.
   if (mPaused) {
     if (mSrcStream) {
       GetSrcMediaStream()->ChangeExplicitBlockerCount(-1);
     }
     DispatchAsyncEvent(NS_LITERAL_STRING("play"));
     switch (mReadyState) {
     case nsIDOMHTMLMediaElement::HAVE_NOTHING:
       DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
@@ -1874,16 +1894,18 @@ NS_IMETHODIMP nsHTMLMediaElement::Play()
       break;
     case nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA:
     case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
       DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
       break;
     }
   }
 
+  SetPlaybackRate(mDefaultPlaybackRate);
+
   mPaused = false;
   mAutoplaying = false;
   // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
   // and our preload status.
   AddRemoveSelfReference();
   UpdatePreloadAction();
 
   return NS_OK;
@@ -2021,391 +2043,33 @@ nsresult nsHTMLMediaElement::BindToTree(
 void nsHTMLMediaElement::UnbindFromTree(bool aDeep,
                                         bool aNullParent)
 {
   if (!mPaused && mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY)
     Pause();
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 }
 
-#ifdef MOZ_RAW
-static const char gRawTypes[2][16] = {
-  "video/x-raw",
-  "video/x-raw-yuv"
-};
-
-static const char* gRawCodecs[1] = {
-  nullptr
-};
-
-static bool IsRawType(const nsACString& aType)
-{
-  if (!MediaDecoder::IsRawEnabled()) {
-    return false;
-  }
-
-  for (uint32_t i = 0; i < ArrayLength(gRawTypes); ++i) {
-    if (aType.EqualsASCII(gRawTypes[i])) {
-      return true;
-    }
-  }
-
-  return false;
-}
-#endif
-#ifdef MOZ_OGG
-// See http://www.rfc-editor.org/rfc/rfc5334.txt for the definitions
-// of Ogg media types and codec types
-const char nsHTMLMediaElement::gOggTypes[3][16] = {
-  "video/ogg",
-  "audio/ogg",
-  "application/ogg"
-};
-
-char const *const nsHTMLMediaElement::gOggCodecs[3] = {
-  "vorbis",
-  "theora",
-  nullptr
-};
-
-char const *const nsHTMLMediaElement::gOggCodecsWithOpus[4] = {
-  "vorbis",
-  "opus",
-  "theora",
-  nullptr
-};
-
-bool
-nsHTMLMediaElement::IsOggType(const nsACString& aType)
-{
-  if (!MediaDecoder::IsOggEnabled()) {
-    return false;
-  }
-
-  for (uint32_t i = 0; i < ArrayLength(gOggTypes); ++i) {
-    if (aType.EqualsASCII(gOggTypes[i])) {
-      return true;
-    }
-  }
-
-  return false;
-}
-#endif
-
-#ifdef MOZ_WAVE
-// See http://www.rfc-editor.org/rfc/rfc2361.txt for the definitions
-// of WAVE media types and codec types. However, the audio/vnd.wave
-// MIME type described there is not used.
-const char nsHTMLMediaElement::gWaveTypes[4][15] = {
-  "audio/x-wav",
-  "audio/wav",
-  "audio/wave",
-  "audio/x-pn-wav"
-};
-
-char const *const nsHTMLMediaElement::gWaveCodecs[2] = {
-  "1", // Microsoft PCM Format
-  nullptr
-};
-
-bool
-nsHTMLMediaElement::IsWaveType(const nsACString& aType)
-{
-  if (!MediaDecoder::IsWaveEnabled()) {
-    return false;
-  }
-
-  for (uint32_t i = 0; i < ArrayLength(gWaveTypes); ++i) {
-    if (aType.EqualsASCII(gWaveTypes[i])) {
-      return true;
-    }
-  }
-
-  return false;
-}
-#endif
-
-#ifdef MOZ_WEBM
-const char nsHTMLMediaElement::gWebMTypes[2][11] = {
-  "video/webm",
-  "audio/webm"
-};
-
-char const *const nsHTMLMediaElement::gWebMCodecs[4] = {
-  "vp8",
-  "vp8.0",
-  "vorbis",
-  nullptr
-};
-
-bool
-nsHTMLMediaElement::IsWebMType(const nsACString& aType)
-{
-  if (!MediaDecoder::IsWebMEnabled()) {
-    return false;
-  }
-
-  for (uint32_t i = 0; i < ArrayLength(gWebMTypes); ++i) {
-    if (aType.EqualsASCII(gWebMTypes[i])) {
-      return true;
-    }
-  }
-
-  return false;
-}
-#endif
-
-#if defined(MOZ_GSTREAMER) || defined(MOZ_WIDGET_GONK)
-char const *const nsHTMLMediaElement::gH264Codecs[9] = {
-  "avc1.42E01E",  // H.264 Constrained Baseline Profile Level 3.0
-  "avc1.42001E",  // H.264 Baseline Profile Level 3.0
-  "avc1.58A01E",  // H.264 Extended Profile Level 3.0
-  "avc1.4D401E",  // H.264 Main Profile Level 3.0
-  "avc1.64001E",  // H.264 High Profile Level 3.0
-  "avc1.64001F",  // H.264 High Profile Level 3.1
-  "mp4v.20.3",    // 3GPP
-  "mp4a.40.2",    // AAC-LC
-  nullptr
-};
-#endif
-
-#ifdef MOZ_GSTREAMER
-const char nsHTMLMediaElement::gH264Types[3][16] = {
-  "video/mp4",
-  "video/3gpp",
-  "video/quicktime",
-};
-
-bool
-nsHTMLMediaElement::IsH264Type(const nsACString& aType)
-{
-  for (uint32_t i = 0; i < ArrayLength(gH264Types); ++i) {
-    if (aType.EqualsASCII(gH264Types[i])) {
-      return true;
-    }
-  }
-  return false;
-}
-#endif
-
-#ifdef MOZ_WIDGET_GONK
-const char nsHTMLMediaElement::gOmxTypes[5][16] = {
-  "audio/mpeg",
-  "audio/mp4",
-  "video/mp4",
-  "video/3gpp",
-  "video/quicktime",
-};
-
-bool
-nsHTMLMediaElement::IsOmxSupportedType(const nsACString& aType)
-{
-  if (!MediaDecoder::IsOmxEnabled()) {
-    return false;
-  }
-
-  for (uint32_t i = 0; i < ArrayLength(gOmxTypes); ++i) {
-    if (aType.EqualsASCII(gOmxTypes[i])) {
-      return true;
-    }
-  }
-
-  return false;
-}
-#endif
-
-#ifdef MOZ_MEDIA_PLUGINS
-bool
-nsHTMLMediaElement::IsMediaPluginsType(const nsACString& aType)
-{
-  if (!MediaDecoder::IsMediaPluginsEnabled()) {
-    return false;
-  }
-
-  static const char* supportedTypes[] = {
-    "audio/mpeg", "audio/mp4", "video/mp4"
-  };
-  for (uint32_t i = 0; i < ArrayLength(supportedTypes); ++i) {
-    if (aType.EqualsASCII(supportedTypes[i])) {
-      return true;
-    }
-  }
-  return false;
-}
-#endif
-
-#ifdef MOZ_DASH
 /* static */
-const char nsHTMLMediaElement::gDASHMPDTypes[1][21] = {
-  "application/dash+xml"
-};
-
-/* static */
-bool
-nsHTMLMediaElement::IsDASHMPDType(const nsACString& aType)
-{
-  if (!MediaDecoder::IsDASHEnabled()) {
-    return false;
-  }
-
-  for (uint32_t i = 0; i < ArrayLength(gDASHMPDTypes); ++i) {
-    if (aType.EqualsASCII(gDASHMPDTypes[i])) {
-      return true;
-    }
-  }
-
-  return false;
-}
-#endif
-
-/* static */
-nsHTMLMediaElement::CanPlayStatus
-nsHTMLMediaElement::CanHandleMediaType(const char* aMIMEType,
-                                       char const *const ** aCodecList)
-{
-#ifdef MOZ_RAW
-  if (IsRawType(nsDependentCString(aMIMEType))) {
-    *aCodecList = gRawCodecs;
-    return CANPLAY_MAYBE;
-  }
-#endif
-#ifdef MOZ_OGG
-  if (IsOggType(nsDependentCString(aMIMEType))) {
-    *aCodecList = MediaDecoder::IsOpusEnabled() ? gOggCodecsWithOpus : gOggCodecs;
-    return CANPLAY_MAYBE;
-  }
-#endif
-#ifdef MOZ_WAVE
-  if (IsWaveType(nsDependentCString(aMIMEType))) {
-    *aCodecList = gWaveCodecs;
-    return CANPLAY_MAYBE;
-  }
-#endif
-#ifdef MOZ_WEBM
-  if (IsWebMType(nsDependentCString(aMIMEType))) {
-    *aCodecList = gWebMCodecs;
-    return CANPLAY_YES;
-  }
-#endif
-#ifdef MOZ_DASH
-  if (IsDASHMPDType(nsDependentCString(aMIMEType))) {
-    // DASH manifest uses WebM codecs only.
-    *aCodecList = gWebMCodecs;
-    return CANPLAY_YES;
-  }
-#endif
-
-#ifdef MOZ_GSTREAMER
-  if (IsH264Type(nsDependentCString(aMIMEType))) {
-    *aCodecList = gH264Codecs;
-    return CANPLAY_MAYBE;
-  }
-#endif
-#ifdef MOZ_WIDGET_GONK
-  if (IsOmxSupportedType(nsDependentCString(aMIMEType))) {
-    *aCodecList = gH264Codecs;
-    return CANPLAY_MAYBE;
-  }
-#endif
-#ifdef MOZ_MEDIA_PLUGINS
-  if (MediaDecoder::IsMediaPluginsEnabled() && GetMediaPluginHost()->FindDecoder(nsDependentCString(aMIMEType), aCodecList))
-    return CANPLAY_MAYBE;
-#endif
-  return CANPLAY_NO;
-}
-
-/* static */
-bool nsHTMLMediaElement::ShouldHandleMediaType(const char* aMIMEType)
-{
-#ifdef MOZ_RAW
-  if (IsRawType(nsDependentCString(aMIMEType)))
-    return true;
-#endif
-#ifdef MOZ_OGG
-  if (IsOggType(nsDependentCString(aMIMEType)))
-    return true;
-#endif
-#ifdef MOZ_WEBM
-  if (IsWebMType(nsDependentCString(aMIMEType)))
-    return true;
-#endif
-#ifdef MOZ_GSTREAMER
-  if (IsH264Type(nsDependentCString(aMIMEType)))
-    return true;
-#endif
-#ifdef MOZ_WIDGET_GONK
-  if (IsOmxSupportedType(nsDependentCString(aMIMEType))) {
-    return true;
-  }
-#endif
-#ifdef MOZ_MEDIA_PLUGINS
-  if (MediaDecoder::IsMediaPluginsEnabled() && GetMediaPluginHost()->FindDecoder(nsDependentCString(aMIMEType), NULL))
-    return true;
-#endif
-  // We should not return true for Wave types, since there are some
-  // Wave codecs actually in use in the wild that we don't support, and
-  // we should allow those to be handled by plugins or helper apps.
-  // Furthermore people can play Wave files on most platforms by other
-  // means.
-  return false;
-}
-
-static bool
-CodecListContains(char const *const * aCodecs, const nsAString& aCodec)
-{
-  for (int32_t i = 0; aCodecs[i]; ++i) {
-    if (aCodec.EqualsASCII(aCodecs[i]))
-      return true;
-  }
-  return false;
-}
-
-/* static */
-nsHTMLMediaElement::CanPlayStatus
+CanPlayStatus
 nsHTMLMediaElement::GetCanPlay(const nsAString& aType)
 {
   nsContentTypeParser parser(aType);
   nsAutoString mimeType;
   nsresult rv = parser.GetType(mimeType);
   if (NS_FAILED(rv))
     return CANPLAY_NO;
 
-  NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
-  char const *const * supportedCodecs;
-  CanPlayStatus status = CanHandleMediaType(mimeTypeUTF8.get(),
-                                            &supportedCodecs);
-  if (status == CANPLAY_NO)
-    return CANPLAY_NO;
-
   nsAutoString codecs;
   rv = parser.GetParameter("codecs", codecs);
-  if (NS_FAILED(rv)) {
-    // Parameter not found or whatever
-    return status;
-  }
-
-  CanPlayStatus result = CANPLAY_YES;
-  // See http://www.rfc-editor.org/rfc/rfc4281.txt for the description
-  // of the 'codecs' parameter
-  nsCharSeparatedTokenizer tokenizer(codecs, ',');
-  bool expectMoreTokens = false;
-  while (tokenizer.hasMoreTokens()) {
-    const nsSubstring& token = tokenizer.nextToken();
-
-    if (!CodecListContains(supportedCodecs, token)) {
-      // Totally unsupported codec
-      return CANPLAY_NO;
-    }
-    expectMoreTokens = tokenizer.lastTokenEndedWithSeparator();
-  }
-  if (expectMoreTokens) {
-    // Last codec name was empty
-    return CANPLAY_NO;
-  }
-  return result;
+
+  NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
+  return DecoderTraits::CanHandleMediaType(mimeTypeUTF8.get(),
+                                           NS_SUCCEEDED(rv),
+                                           codecs);
 }
 
 NS_IMETHODIMP
 nsHTMLMediaElement::CanPlayType(const nsAString& aType, nsAString& aResult)
 {
   switch (GetCanPlay(aType)) {
   case CANPLAY_NO:
     aResult.Truncate();
@@ -2416,104 +2080,82 @@ nsHTMLMediaElement::CanPlayType(const ns
   default:
   case CANPLAY_MAYBE:
     aResult.AssignLiteral("maybe");
     break;
   }
   return NS_OK;
 }
 
-#ifdef MOZ_GSTREAMER
-bool
-nsHTMLMediaElement::IsGStreamerSupportedType(const nsACString& aMimeType)
-{
-  if (!MediaDecoder::IsGStreamerEnabled())
-    return false;
-  if (IsH264Type(aMimeType))
-    return true;
-  if (!Preferences::GetBool("media.prefer-gstreamer", false))
-    return false;
-#ifdef MOZ_WEBM
-  if (IsWebMType(aMimeType))
-    return true;
-#endif
-#ifdef MOZ_OGG
-  if (IsOggType(aMimeType))
-    return true;
-#endif
-  return false;
-}
-#endif
-
 already_AddRefed<MediaDecoder>
 nsHTMLMediaElement::CreateDecoder(const nsACString& aType)
 {
 
 #ifdef MOZ_GSTREAMER
   // When enabled, use GStreamer for H.264, but not for codecs handled by our
   // bundled decoders, unless the "media.prefer-gstreamer" pref is set.
-  if (IsGStreamerSupportedType(aType)) {
+  if (DecoderTraits::IsGStreamerSupportedType(aType)) {
     nsRefPtr<GStreamerDecoder> decoder = new GStreamerDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
 
 #ifdef MOZ_RAW
-  if (IsRawType(aType)) {
+  if (DecoderTraits::IsRawType(aType)) {
     nsRefPtr<RawDecoder> decoder = new RawDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
 #ifdef MOZ_OGG
-  if (IsOggType(aType)) {
+  if (DecoderTraits::IsOggType(aType)) {
     nsRefPtr<OggDecoder> decoder = new OggDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
 #ifdef MOZ_WAVE
-  if (IsWaveType(aType)) {
+  if (DecoderTraits::IsWaveType(aType)) {
     nsRefPtr<WaveDecoder> decoder = new WaveDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
 #ifdef MOZ_WIDGET_GONK
-  if (IsOmxSupportedType(aType)) {
+  if (DecoderTraits::IsOmxSupportedType(aType)) {
     nsRefPtr<MediaOmxDecoder> decoder = new MediaOmxDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
 #ifdef MOZ_MEDIA_PLUGINS
   if (MediaDecoder::IsMediaPluginsEnabled() && GetMediaPluginHost()->FindDecoder(aType, NULL)) {
     nsRefPtr<MediaPluginDecoder> decoder = new MediaPluginDecoder(aType);
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
 #ifdef MOZ_WEBM
-  if (IsWebMType(aType)) {
+  if (DecoderTraits::IsWebMType(aType)) {
     nsRefPtr<WebMDecoder> decoder = new WebMDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
 
 #ifdef MOZ_DASH
-  if (IsDASHMPDType(aType)) {
+  if (DecoderTraits::IsDASHMPDType(aType)) {
     nsRefPtr<DASHDecoder> decoder = new DASHDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
 
   return nullptr;
@@ -3710,16 +3352,92 @@ void nsHTMLMediaElement::GetMimeType(nsC
 
 void nsHTMLMediaElement::NotifyAudioAvailableListener()
 {
   if (mDecoder) {
     mDecoder->NotifyAudioAvailableListener();
   }
 }
 
+static double ClampPlaybackRate(double aPlaybackRate)
+{
+  if (aPlaybackRate == 0.0) {
+    return aPlaybackRate;
+  }
+  if (NS_ABS(aPlaybackRate) < MIN_PLAYBACKRATE) {
+    return aPlaybackRate < 0 ? -MIN_PLAYBACKRATE : MIN_PLAYBACKRATE;
+  }
+  if (NS_ABS(aPlaybackRate) > MAX_PLAYBACKRATE) {
+    return aPlaybackRate < 0 ? -MAX_PLAYBACKRATE : MAX_PLAYBACKRATE;
+  }
+  return aPlaybackRate;
+}
+
+/* attribute double defaultPlaybackRate; */
+NS_IMETHODIMP nsHTMLMediaElement::GetDefaultPlaybackRate(double* aDefaultPlaybackRate)
+{
+  *aDefaultPlaybackRate = mDefaultPlaybackRate;
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsHTMLMediaElement::SetDefaultPlaybackRate(double aDefaultPlaybackRate)
+{
+  if (aDefaultPlaybackRate < 0) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  mDefaultPlaybackRate = ClampPlaybackRate(aDefaultPlaybackRate);
+  DispatchAsyncEvent(NS_LITERAL_STRING("ratechange"));
+  return NS_OK;
+}
+
+/* attribute double playbackRate; */
+NS_IMETHODIMP nsHTMLMediaElement::GetPlaybackRate(double* aPlaybackRate)
+{
+  *aPlaybackRate = mPlaybackRate;
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsHTMLMediaElement::SetPlaybackRate(double aPlaybackRate)
+{
+  if (aPlaybackRate < 0) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  mPlaybackRate = ClampPlaybackRate(aPlaybackRate);
+
+  if (mPlaybackRate < 0 ||
+      mPlaybackRate > THRESHOLD_HIGH_PLAYBACKRATE_AUDIO ||
+      mPlaybackRate < THRESHOLD_LOW_PLAYBACKRATE_AUDIO) {
+    SetMutedInternal(true);
+  } else {
+    SetMutedInternal(false);
+  }
+
+  if (mDecoder) {
+    mDecoder->SetPlaybackRate(mPlaybackRate);
+  }
+  DispatchAsyncEvent(NS_LITERAL_STRING("ratechange"));
+  return NS_OK;
+}
+
+/* attribute bool mozPreservesPitch; */
+NS_IMETHODIMP nsHTMLMediaElement::GetMozPreservesPitch(bool* aPreservesPitch)
+{
+  *aPreservesPitch = mPreservesPitch;
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsHTMLMediaElement::SetMozPreservesPitch(bool aPreservesPitch)
+{
+  mPreservesPitch = aPreservesPitch;
+  mDecoder->SetPreservesPitch(aPreservesPitch);
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsHTMLMediaElement::GetMozAudioChannelType(nsAString& aString)
 {
   switch (mAudioChannelType) {
     case AUDIO_CHANNEL_NORMAL:
       aString.AssignLiteral("normal");
       break;
     case AUDIO_CHANNEL_CONTENT:
@@ -3740,36 +3458,39 @@ nsHTMLMediaElement::GetMozAudioChannelTy
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLMediaElement::SetMozAudioChannelType(const nsAString& aString)
 {
-  if (mDecoder) {
-    return NS_ERROR_FAILURE;
-  }
+  AudioChannelType tmpType;
 
   if (aString.EqualsASCII("normal")) {
-    mAudioChannelType = AUDIO_CHANNEL_NORMAL;
+    tmpType = AUDIO_CHANNEL_NORMAL;
   } else if (aString.EqualsASCII("content")) {
-    mAudioChannelType = AUDIO_CHANNEL_CONTENT;
+    tmpType = AUDIO_CHANNEL_CONTENT;
   } else if (aString.EqualsASCII("notification")) {
-    mAudioChannelType = AUDIO_CHANNEL_NOTIFICATION;
+    tmpType = AUDIO_CHANNEL_NOTIFICATION;
   } else if (aString.EqualsASCII("alarm")) {
-    mAudioChannelType = AUDIO_CHANNEL_ALARM;
+    tmpType = AUDIO_CHANNEL_ALARM;
   } else if (aString.EqualsASCII("telephony")) {
-    mAudioChannelType = AUDIO_CHANNEL_TELEPHONY;
+    tmpType = AUDIO_CHANNEL_TELEPHONY;
   } else if (aString.EqualsASCII("publicnotification")) {
-    mAudioChannelType = AUDIO_CHANNEL_PUBLICNOTIFICATION;
+    tmpType = AUDIO_CHANNEL_PUBLICNOTIFICATION;
   } else {
     return NS_ERROR_FAILURE;
   }
 
+  if (tmpType != mAudioChannelType && mDecoder) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mAudioChannelType = tmpType;
   return NS_OK;
 }
 
 ImageContainer* nsHTMLMediaElement::GetImageContainer()
 {
   VideoFrameContainer* container = GetVideoFrameContainer();
   return container ? container->GetImageContainer() : nullptr;
 }
--- a/content/html/content/src/nsHTMLUnknownElement.h
+++ b/content/html/content/src/nsHTMLUnknownElement.h
@@ -10,16 +10,19 @@
 
 class nsHTMLUnknownElement : public nsGenericHTMLElement
                            , public nsIDOMHTMLUnknownElement
 {
 public:
   nsHTMLUnknownElement(already_AddRefed<nsINodeInfo> aNodeInfo)
     : nsGenericHTMLElement(aNodeInfo)
   {
+    if (NodeInfo()->Equals(nsGkAtoms::bdi)) {
+      SetHasDirAuto();
+    }
   }
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIDOMNode
   NS_FORWARD_NSIDOMNODE_TO_NSINODE
 
--- a/content/html/content/test/test_bug660663.html
+++ b/content/html/content/test/test_bug660663.html
@@ -16,16 +16,15 @@ https://bugzilla.mozilla.org/show_bug.cg
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 /** Test for Bug 660663 **/
 reflectLimitedEnumerated({
   element: document.createElement("div"),
   attribute: "dir",
-  validValues: ["ltr", "rtl"],
-  invalidValues: ["cheesecake", ""],
-  unsupportedValues: ["auto"]
+  validValues: ["ltr", "rtl", "auto"],
+  invalidValues: ["cheesecake", ""]
 });
 </script>
 </pre>
 </body>
 </html>
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -167,29 +167,30 @@ RemoveFromAgentSheets(nsCOMArray<nsIStyl
       aAgentSheets.RemoveObjectAt(i);
     }
   }
 
   return NS_OK;
 }
 
 nsresult
-NS_NewHTMLDocument(nsIDocument** aInstancePtrResult)
+NS_NewHTMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData)
 {
   nsHTMLDocument* doc = new nsHTMLDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_OUT_OF_MEMORY);
 
   NS_ADDREF(doc);
   nsresult rv = doc->Init();
 
   if (NS_FAILED(rv)) {
     NS_RELEASE(doc);
   }
 
   *aInstancePtrResult = doc;
+  doc->SetLoadedAsData(aLoadedAsData);
 
   return rv;
 }
 
   // NOTE! nsDocument::operator new() zeroes out all members, so don't
   // bother initializing members to 0.
 
 nsHTMLDocument::nsHTMLDocument()
@@ -1510,21 +1511,19 @@ nsHTMLDocument::Open(const nsAString& aC
     // Now make sure we're not flagged as the initial document anymore, now
     // that we've had stuff done to us.  From now on, if anyone tries to
     // document.open() us, they get a new inner window.
     SetIsInitialDocument(false);
 
     nsCOMPtr<nsIScriptGlobalObject> newScope(do_QueryReferent(mScopeObject));
     if (oldScope && newScope != oldScope) {
       nsIXPConnect *xpc = nsContentUtils::XPConnect();
-      nsCOMPtr<nsIXPConnectJSObjectHolder> ignored;
       rv = xpc->ReparentWrappedNativeIfFound(cx, oldScope->GetGlobalJSObject(),
                                              newScope->GetGlobalJSObject(),
-                                             static_cast<nsINode*>(this),
-                                             getter_AddRefs(ignored));
+                                             static_cast<nsINode*>(this));
       NS_ENSURE_SUCCESS(rv, rv);
       rv = xpc->RescueOrphansInScope(cx, oldScope->GetGlobalJSObject());
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // Call Reset(), this will now do the full reset
   Reset(channel, group);
--- a/content/media/AudioSampleFormat.h
+++ b/content/media/AudioSampleFormat.h
@@ -125,11 +125,30 @@ ConvertAudioSamplesWithScale(const int16
     }
     return;
   }
   for (int i = 0; i < aCount; ++i) {
     aTo[i] = FloatToAudioSample<int16_t>(AudioSampleToFloat(aFrom[i])*aScale);
   }
 }
 
+// In place audio sample scaling.
+inline void
+ScaleAudioSamples(float* aBuffer, int aCount, float aScale)
+{
+  for (int32_t i = 0; i < aCount; ++i) {
+    aBuffer[i] *= aScale;
+  }
+}
+
+
+inline void
+ScaleAudioSamples(short* aBuffer, int aCount, float aScale)
+{
+  int32_t volume = int32_t(1 << 16) * aScale;
+  for (int32_t i = 0; i < aCount; ++i) {
+    aBuffer[i] = short((int32_t(aBuffer[i]) * volume) >> 16);
+  }
+}
+
 } // namespace mozilla
 
 #endif /* MOZILLA_AUDIOSAMPLEFORMAT_H_ */
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -3,26 +3,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include <stdio.h>
 #include <math.h>
 #include "prlog.h"
 #include "prmem.h"
 #include "prdtoa.h"
-#include "nsAutoPtr.h"
 #include "AudioStream.h"
 #include "nsAlgorithm.h"
 #include "VideoUtils.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Mutex.h"
 extern "C" {
 #include "sydneyaudio/sydney_audio.h"
 }
-#include "nsThreadUtils.h"
 #include "mozilla/Preferences.h"
 
 #if defined(MOZ_CUBEB)
 #include "nsAutoRef.h"
 #include "cubeb/cubeb.h"
 
 template <>
 class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream>
@@ -46,36 +44,37 @@ PRLogModuleInfo* gAudioStreamLog = nullp
 static const uint32_t FAKE_BUFFER_SIZE = 176400;
 
 // Number of milliseconds per second.
 static const int64_t MS_PER_S = 1000;
 
 class nsNativeAudioStream : public AudioStream
 {
  public:
-  NS_DECL_ISUPPORTS
-
   ~nsNativeAudioStream();
   nsNativeAudioStream();
 
   nsresult Init(int32_t aNumChannels, int32_t aRate,
                 const dom::AudioChannelType aAudioChannelType);
   void Shutdown();
   nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames);
   uint32_t Available();
   void SetVolume(double aVolume);
   void Drain();
   void Pause();
   void Resume();
   int64_t GetPosition();
   int64_t GetPositionInFrames();
+  int64_t GetPositionInFramesInternal();
   bool IsPaused();
   int32_t GetMinWriteSize();
 
  private:
+  int32_t WriteToBackend(const float* aBuffer, uint32_t aFrames);
+  int32_t WriteToBackend(const short* aBuffer, uint32_t aFrames);
 
   double mVolume;
   void* mAudioHandle;
 
   // True if this audio stream is paused.
   bool mPaused;
 
   // True if this stream has encountered an error.
@@ -171,16 +170,23 @@ static sa_stream_type_t ConvertChannelTo
     case dom::AUDIO_CHANNEL_PUBLICNOTIFICATION:
       return SA_STREAM_TYPE_ENFORCED_AUDIBLE;
     default:
       NS_ERROR("The value of AudioChannelType is invalid");
       return SA_STREAM_TYPE_MAX;
   }
 }
 
+AudioStream::AudioStream()
+: mInRate(0),
+  mOutRate(0),
+  mChannels(0),
+  mAudioClock(this)
+{}
+
 void AudioStream::InitLibrary()
 {
 #ifdef PR_LOGGING
   gAudioStreamLog = PR_NewLogModule("AudioStream");
 #endif
   gAudioPrefsLock = new Mutex("AudioStream::gAudioPrefsLock");
   PrefChanged(PREF_VOLUME_SCALE, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
@@ -204,64 +210,97 @@ void AudioStream::ShutdownLibrary()
 #if defined(MOZ_CUBEB)
   if (gCubebContext) {
     cubeb_destroy(gCubebContext);
     gCubebContext = nullptr;
   }
 #endif
 }
 
-nsIThread *
-AudioStream::GetThread()
+AudioStream::~AudioStream()
+{
+}
+
+bool AudioStream::EnsureTimeStretcherInitialized()
 {
-  if (!mAudioPlaybackThread) {
-    NS_NewNamedThread("Audio Stream",
-                      getter_AddRefs(mAudioPlaybackThread),
-                      nullptr,
-                      MEDIA_THREAD_STACK_SIZE);
+  if (mTimeStretcher)
+    return true;
+  soundtouch::SoundTouch* state = new soundtouch::SoundTouch();
+  if (!state) {
+    return false;
   }
-  return mAudioPlaybackThread;
+  mTimeStretcher.own(state);
+  mTimeStretcher->setSampleRate(mInRate);
+  mTimeStretcher->setChannels(mChannels);
+  mTimeStretcher->setPitch(1.0);
+  return true;
 }
 
-class AsyncShutdownPlaybackThread : public nsRunnable
+nsresult AudioStream::SetPlaybackRate(double aPlaybackRate)
 {
-public:
-  AsyncShutdownPlaybackThread(nsIThread* aThread) : mThread(aThread) {}
-  NS_IMETHODIMP Run() { return mThread->Shutdown(); }
-private:
-  nsCOMPtr<nsIThread> mThread;
-};
+  NS_ASSERTION(aPlaybackRate > 0.0,
+               "Can't handle negative or null playbackrate in the AudioStream.");
+  // Avoid instantiating the resampler if we are not changing the playback rate.
+  if (aPlaybackRate == mAudioClock.GetPlaybackRate()) {
+    return NS_OK;
+  }
+  mAudioClock.SetPlaybackRate(aPlaybackRate);
+  mOutRate = mInRate / aPlaybackRate;
+  if (!EnsureTimeStretcherInitialized()) {
+    return NS_ERROR_FAILURE;
+  }
+  if (mAudioClock.GetPreservesPitch()) {
+    mTimeStretcher->setTempo(aPlaybackRate);
+    mTimeStretcher->setRate(1.0f);
+  } else {
+    mTimeStretcher->setTempo(1.0f);
+    mTimeStretcher->setRate(aPlaybackRate);
+  }
+  return NS_OK;
+}
 
-AudioStream::~AudioStream()
+nsresult AudioStream::SetPreservesPitch(bool aPreservesPitch)
 {
-  if (mAudioPlaybackThread) {
-    nsCOMPtr<nsIRunnable> event = new AsyncShutdownPlaybackThread(mAudioPlaybackThread);
-    NS_DispatchToMainThread(event);
+  // Avoid instantiating the timestretcher instance if not needed.
+  if (aPreservesPitch == mAudioClock.GetPreservesPitch()) {
+    return NS_OK;
+  }
+  if (!EnsureTimeStretcherInitialized()) {
+    return NS_ERROR_FAILURE;
   }
+  if (aPreservesPitch == true) {
+    mTimeStretcher->setTempo(mAudioClock.GetPlaybackRate());
+    mTimeStretcher->setRate(1.0f);
+  } else {
+    mTimeStretcher->setTempo(1.0f);
+    mTimeStretcher->setRate(mAudioClock.GetPlaybackRate());
+  }
+
+  mAudioClock.SetPreservesPitch(aPreservesPitch);
+
+  return NS_OK;
 }
 
 nsNativeAudioStream::nsNativeAudioStream() :
   mVolume(1.0),
   mAudioHandle(0),
   mPaused(false),
   mInError(false)
 {
 }
 
 nsNativeAudioStream::~nsNativeAudioStream()
 {
   Shutdown();
 }
 
-NS_IMPL_THREADSAFE_ISUPPORTS0(nsNativeAudioStream)
-
 nsresult nsNativeAudioStream::Init(int32_t aNumChannels, int32_t aRate,
                                    const dom::AudioChannelType aAudioChannelType)
 {
-  mRate = aRate;
+  mInRate = mOutRate = aRate;
   mChannels = aNumChannels;
 
   if (sa_stream_create_pcm(reinterpret_cast<sa_stream_t**>(&mAudioHandle),
                            NULL,
                            SA_MODE_WRONLY,
                            SA_PCM_FORMAT_S16_NE,
                            aRate,
                            aNumChannels) != SA_SUCCESS) {
@@ -284,46 +323,80 @@ nsresult nsNativeAudioStream::Init(int32
     sa_stream_destroy(static_cast<sa_stream_t*>(mAudioHandle));
     mAudioHandle = nullptr;
     mInError = true;
     PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsNativeAudioStream: sa_stream_open error"));
     return NS_ERROR_FAILURE;
   }
   mInError = false;
 
+  mAudioClock.Init();
+
   return NS_OK;
 }
 
 void nsNativeAudioStream::Shutdown()
 {
   if (!mAudioHandle)
     return;
 
   sa_stream_destroy(static_cast<sa_stream_t*>(mAudioHandle));
   mAudioHandle = nullptr;
   mInError = true;
 }
 
+int32_t nsNativeAudioStream::WriteToBackend(const AudioDataValue* aBuffer, uint32_t aSamples)
+{
+  double scaledVolume = GetVolumeScale() * mVolume;
+
+  nsAutoArrayPtr<short> outputBuffer(new short[aSamples]);
+  ConvertAudioSamplesWithScale(aBuffer, outputBuffer.get(), aSamples, scaledVolume);
+
+  if (sa_stream_write(static_cast<sa_stream_t*>(mAudioHandle),
+                      outputBuffer,
+                      aSamples * sizeof(short)) != SA_SUCCESS) {
+    return -1;
+  }
+  mAudioClock.UpdateWritePosition(aSamples / mChannels);
+  return aSamples;
+}
+
 nsresult nsNativeAudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames)
 {
   NS_ASSERTION(!mPaused, "Don't write audio when paused, you'll block");
 
   if (mInError)
     return NS_ERROR_FAILURE;
 
   uint32_t samples = aFrames * mChannels;
-  nsAutoArrayPtr<short> s_data(new short[samples]);
-
-  float scaled_volume = float(GetVolumeScale() * mVolume);
-  ConvertAudioSamplesWithScale(aBuf, s_data.get(), samples, scaled_volume);
+  int32_t written = -1;
 
-  if (sa_stream_write(static_cast<sa_stream_t*>(mAudioHandle),
-                      s_data.get(),
-                      samples * sizeof(short)) != SA_SUCCESS)
-  {
+  if (mInRate != mOutRate) {
+    if (!EnsureTimeStretcherInitialized()) {
+      return NS_ERROR_FAILURE;
+    }
+    mTimeStretcher->putSamples(aBuf, aFrames);
+    uint32_t numFrames = mTimeStretcher->numSamples();
+    uint32_t arraySize = numFrames * mChannels * sizeof(AudioDataValue);
+    nsAutoArrayPtr<AudioDataValue> data(new AudioDataValue[arraySize]);
+    uint32_t framesAvailable = mTimeStretcher->receiveSamples(data, numFrames);
+    NS_ASSERTION(mTimeStretcher->numSamples() == 0,
+                 "We did not get all the data from the SoundTouch pipeline.");
+    // It is possible to have nothing to write: the data are in the processing
+    // pipeline, and will be written to the backend next time.
+    if (framesAvailable) {
+      written = WriteToBackend(data, framesAvailable * mChannels);
+    } else {
+      written = 0;
+    }
+  } else {
+    written = WriteToBackend(aBuf, samples);
+  }
+
+  if (written == -1) {
     PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsNativeAudioStream: sa_stream_write error"));
     mInError = true;
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 uint32_t nsNativeAudioStream::Available()
@@ -352,16 +425,36 @@ void nsNativeAudioStream::SetVolume(doub
   mVolume = aVolume;
 #endif
 }
 
 void nsNativeAudioStream::Drain()
 {
   NS_ASSERTION(!mPaused, "Don't drain audio when paused, it won't finish!");
 
+  // Write all the frames still in the time stretcher pipeline.
+  if (mTimeStretcher) {
+    uint32_t numFrames = mTimeStretcher->numSamples();
+    uint32_t arraySize = numFrames * mChannels * sizeof(AudioDataValue);
+    nsAutoArrayPtr<AudioDataValue> data(new AudioDataValue[arraySize]);
+    uint32_t framesAvailable = mTimeStretcher->receiveSamples(data, numFrames);
+    int32_t written = 0;
+    if (framesAvailable) {
+      written = WriteToBackend(data, framesAvailable * mChannels);
+    }
+
+    if (written == -1) {
+      PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsNativeAudioStream: sa_stream_write error"));
+      mInError = true;
+    }
+
+    NS_ASSERTION(mTimeStretcher->numSamples() == 0,
+                 "We did not get all the data from the SoundTouch pipeline.");
+  }
+
   if (mInError)
     return;
 
   int r = sa_stream_drain(static_cast<sa_stream_t*>(mAudioHandle));
   if (r != SA_SUCCESS && r != SA_ERROR_INVALID) {
     PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsNativeAudioStream: sa_stream_drain error"));
     mInError = true;
   }
@@ -380,25 +473,26 @@ void nsNativeAudioStream::Resume()
   if (mInError)
     return;
   mPaused = false;
   sa_stream_resume(static_cast<sa_stream_t*>(mAudioHandle));
 }
 
 int64_t nsNativeAudioStream::GetPosition()
 {
-  int64_t position = GetPositionInFrames();
-  if (position >= 0) {
-    return ((USECS_PER_S * position) / mRate);
-  }
-  return -1;
+  return mAudioClock.GetPosition();
 }
 
 int64_t nsNativeAudioStream::GetPositionInFrames()
 {
+  return mAudioClock.GetPositionInFrames();
+}
+
+int64_t nsNativeAudioStream::GetPositionInFramesInternal()
+{
   if (mInError) {
     return -1;
   }
 
   sa_position_t positionType = SA_POSITION_WRITE_SOFTWARE;
 #if defined(XP_WIN)
   positionType = SA_POSITION_WRITE_HARDWARE;
 #endif
@@ -493,32 +587,31 @@ private:
   uint32_t mCapacity;
   uint32_t mStart;
   uint32_t mCount;
 };
 
 class nsBufferedAudioStream : public AudioStream
 {
  public:
-  NS_DECL_ISUPPORTS
-
   nsBufferedAudioStream();
   ~nsBufferedAudioStream();
 
   nsresult Init(int32_t aNumChannels, int32_t aRate,
                 const dom::AudioChannelType aAudioChannelType);
   void Shutdown();
   nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames);
   uint32_t Available();
   void SetVolume(double aVolume);
   void Drain();
   void Pause();
   void Resume();
   int64_t GetPosition();
   int64_t GetPositionInFrames();
+  int64_t GetPositionInFramesInternal();
   bool IsPaused();
   int32_t GetMinWriteSize();
 
 private:
   static long DataCallback_S(cubeb_stream*, void* aThis, void* aBuffer, long aFrames)
   {
     return static_cast<nsBufferedAudioStream*>(aThis)->DataCallback(aBuffer, aFrames);
   }
@@ -526,16 +619,21 @@ private:
   static void StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState)
   {
     static_cast<nsBufferedAudioStream*>(aThis)->StateCallback(aState);
   }
 
   long DataCallback(void* aBuffer, long aFrames);
   void StateCallback(cubeb_state aState);
 
+  long GetUnprocessed(void* aBuffer, long aFrames);
+
+  long GetTimeStretched(void* aBuffer, long aFrames);
+
+
   // Shared implementation of underflow adjusted position calculation.
   // Caller must own the monitor.
   int64_t GetPositionInFramesUnlocked();
 
   // The monitor is held to protect all access to member variables.  Write()
   // waits while mBuffer is full; DataCallback() notifies as it consumes
   // data from mBuffer.  Drain() waits while mState is DRAINING;
   // StateCallback() notifies when mState is DRAINED.
@@ -555,16 +653,27 @@ private:
   double mVolume;
 
   // Owning reference to a cubeb_stream.  cubeb_stream_destroy is called by
   // nsAutoRef's destructor.
   nsAutoRef<cubeb_stream> mCubebStream;
 
   uint32_t mBytesPerFrame;
 
+  uint32_t BytesToFrames(uint32_t aBytes) {
+    NS_ASSERTION(aBytes % mBytesPerFrame == 0,
+                 "Byte count not aligned on frames size.");
+    return aBytes / mBytesPerFrame;
+  }
+
+  uint32_t FramesToBytes(uint32_t aFrames) {
+    return aFrames * mBytesPerFrame;
+  }
+
+
   enum StreamState {
     INITIALIZED, // Initialized, playback has not begun.
     STARTED,     // Started by a call to Write() (iff INITIALIZED) or Resume().
     STOPPED,     // Stopped by a call to Pause().
     DRAINING,    // Drain requested.  DataCallback will indicate end of stream
                  // once the remaining contents of mBuffer are requested by
                  // cubeb, after which StateCallback will indicate drain
                  // completion.
@@ -593,57 +702,57 @@ nsBufferedAudioStream::nsBufferedAudioSt
 {
 }
 
 nsBufferedAudioStream::~nsBufferedAudioStream()
 {
   Shutdown();
 }
 
-NS_IMPL_THREADSAFE_ISUPPORTS0(nsBufferedAudioStream)
-
 nsresult
 nsBufferedAudioStream::Init(int32_t aNumChannels, int32_t aRate,
                             const dom::AudioChannelType aAudioChannelType)
 {
   cubeb* cubebContext = GetCubebContext();
 
   if (!cubebContext || aNumChannels < 0 || aRate < 0) {
     return NS_ERROR_FAILURE;
   }
 
-  mRate = aRate;
+  mInRate = mOutRate = aRate;
   mChannels = aNumChannels;
 
   cubeb_stream_params params;
   params.rate = aRate;
   params.channels = aNumChannels;
   if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
     params.format = CUBEB_SAMPLE_S16NE;
   } else {
     params.format = CUBEB_SAMPLE_FLOAT32NE;
   }
   mBytesPerFrame = sizeof(AudioDataValue) * aNumChannels;
 
+  mAudioClock.Init();
+
   {
     cubeb_stream* stream;
     if (cubeb_stream_init(cubebContext, &stream, "nsBufferedAudioStream", params,
                           GetCubebLatency(), DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
       mCubebStream.own(stream);
     }
   }
 
   if (!mCubebStream) {
     return NS_ERROR_FAILURE;
   }
 
   // Size mBuffer for one second of audio.  This value is arbitrary, and was
   // selected based on the observed behaviour of the existing AudioStream
   // implementations.
-  uint32_t bufferLimit = aRate * mBytesPerFrame;
+  uint32_t bufferLimit = FramesToBytes(aRate);
   NS_ABORT_IF_FALSE(bufferLimit % mBytesPerFrame == 0, "Must buffer complete frames");
   mBuffer.SetCapacity(bufferLimit);
 
   return NS_OK;
 }
 
 void
 nsBufferedAudioStream::Shutdown()
@@ -658,24 +767,26 @@ nsBufferedAudioStream::Shutdown()
 
 nsresult
 nsBufferedAudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames)
 {
   MonitorAutoLock mon(mMonitor);
   if (!mCubebStream || mState == ERRORED) {
     return NS_ERROR_FAILURE;
   }
-  NS_ASSERTION(mState == INITIALIZED || mState == STARTED, "Stream write in unexpected state.");
+  NS_ASSERTION(mState == INITIALIZED || mState == STARTED,
+    "Stream write in unexpected state.");
 
   const uint8_t* src = reinterpret_cast<const uint8_t*>(aBuf);
-  uint32_t bytesToCopy = aFrames * mBytesPerFrame;
+  uint32_t bytesToCopy = FramesToBytes(aFrames);
 
   while (bytesToCopy > 0) {
     uint32_t available = NS_MIN(bytesToCopy, mBuffer.Available());
-    NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames.");
+    NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0,
+        "Must copy complete frames.");
 
     mBuffer.AppendElements(src, available);
     src += available;
     bytesToCopy -= available;
 
     if (mState != STARTED) {
       int r;
       {
@@ -697,17 +808,17 @@ nsBufferedAudioStream::Write(const Audio
   return NS_OK;
 }
 
 uint32_t
 nsBufferedAudioStream::Available()
 {
   MonitorAutoLock mon(mMonitor);
   NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Buffer invariant violated.");
-  return mBuffer.Available() / mBytesPerFrame;
+  return BytesToFrames(mBuffer.Available());
 }
 
 int32_t
 nsBufferedAudioStream::GetMinWriteSize()
 {
   return 1;
 }
 
@@ -766,39 +877,40 @@ nsBufferedAudioStream::Resume()
   if (mState != ERRORED && r == CUBEB_OK) {
     mState = STARTED;
   }
 }
 
 int64_t
 nsBufferedAudioStream::GetPosition()
 {
-  MonitorAutoLock mon(mMonitor);
-  int64_t frames = GetPositionInFramesUnlocked();
-  if (frames >= 0) {