Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Wed, 02 Jan 2013 19:35:43 -0800
changeset 117370 6955309291ee4afe219fee09987f3838ccf5ac83
parent 117369 c80bd4e618c88b2966b97f9c89fd8e4c858cced7 (current diff)
parent 117342 4e18ac9b51e271770fd67b1e8dd5c4eb7d8a4f73 (diff)
child 117371 801ba75ac563f243ed653647554c333b8cf34820
child 117421 d8603eadb9d01231f3afe42cc81074b98394b56a
child 117940 5d2e27411f1880db908abc4a1d6368d03ef95efc
child 127172 5f74daeb16b70ae17b7587331212ebd95588d109
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
6955309291ee / 20.0a1 / 20130103030946 / files
nightly linux64
6955309291ee / 20.0a1 / 20130103030946 / files
nightly mac
6955309291ee / 20.0a1 / 20130103030946 / files
nightly win32
6955309291ee / 20.0a1 / 20130103030946 / files
nightly win64
6955309291ee / 20.0a1 / 20130103030946 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to s-c.
browser/components/preferences/in-content/tests/privacypane_tests.js
browser/components/preferences/tests/privacypane_tests.js
browser/config/mozconfigs/macosx32/debug
browser/config/tooltool-manifests/macosx32/releng.manifest
browser/themes/gnomestripe/devtools/dock-window.png
browser/themes/pinstripe/devtools/dock-window.png
browser/themes/winstripe/devtools/dock-window.png
build/macosx/mozconfig.leopard
content/base/src/nsDOMDocumentType.cpp
content/base/src/nsDOMDocumentType.h
content/html/content/src/nsHTMLBodyElement.cpp
content/html/content/src/nsHTMLDataListElement.cpp
content/html/content/src/nsHTMLDivElement.cpp
content/html/content/src/nsHTMLDivElement.h
content/html/content/src/nsHTMLElement.cpp
content/html/content/src/nsHTMLFontElement.cpp
content/html/content/src/nsHTMLFrameSetElement.cpp
content/html/content/src/nsHTMLFrameSetElement.h
content/html/content/src/nsHTMLHeadingElement.cpp
content/html/content/src/nsHTMLLabelElement.cpp
content/html/content/src/nsHTMLLabelElement.h
content/html/content/src/nsHTMLTitleElement.cpp
content/html/content/src/nsHTMLUnknownElement.cpp
content/html/content/src/nsHTMLUnknownElement.h
content/html/content/test/test_bug549475.html
content/svg/content/src/nsSVGStylableElement.cpp
content/svg/content/src/nsSVGStylableElement.h
dom/encoding/TextDecoder.h
dom/interfaces/css/nsIDOMNSRGBAColor.idl
dom/interfaces/css/nsIDOMRGBColor.idl
dom/interfaces/svg/nsIDOMSVGAngle.idl
dom/interfaces/svg/nsIDOMSVGAnimPresAspRatio.idl
dom/interfaces/svg/nsIDOMSVGAnimTransformList.idl
dom/interfaces/svg/nsIDOMSVGAnimatedAngle.idl
dom/interfaces/svg/nsIDOMSVGAnimatedBoolean.idl
dom/interfaces/svg/nsIDOMSVGAnimatedLengthList.idl
dom/interfaces/svg/nsIDOMSVGAnimatedNumberList.idl
dom/interfaces/svg/nsIDOMSVGLengthList.idl
dom/interfaces/svg/nsIDOMSVGMatrix.idl
dom/interfaces/svg/nsIDOMSVGNumberList.idl
dom/interfaces/svg/nsIDOMSVGPathSeg.idl
dom/interfaces/svg/nsIDOMSVGPathSegList.idl
dom/interfaces/svg/nsIDOMSVGPoint.idl
dom/interfaces/svg/nsIDOMSVGPointList.idl
dom/interfaces/svg/nsIDOMSVGPresAspectRatio.idl
dom/interfaces/svg/nsIDOMSVGStylable.idl
dom/interfaces/svg/nsIDOMSVGTransform.idl
dom/interfaces/svg/nsIDOMSVGTransformList.idl
layout/reftests/svg/style-property-not-on-script-element-01.svg
mobile/android/base/resources/drawable/abouthome_divider.xml
tools/dmdv/Makefile.in
tools/dmdv/README
tools/dmdv/dmdv.h
tools/dmdv/dmdv.patch
--- a/Makefile.in
+++ b/Makefile.in
@@ -145,23 +145,16 @@ ifdef MOZ_SYMBOLS_EXTRA_BUILDID
 EXTRA_BUILDID := -$(MOZ_SYMBOLS_EXTRA_BUILDID)
 endif
 
 SYMBOL_INDEX_NAME = \
   $(MOZ_APP_NAME)-$(MOZ_APP_VERSION)-$(OS_TARGET)-$(BUILDID)-$(CPU_ARCH)$(EXTRA_BUILDID)-symbols.txt
 
 buildsymbols:
 ifdef MOZ_CRASHREPORTER
-ifdef USE_ELF_HACK
-    ifeq (mobile,$(MOZ_BUILD_APP))
-		$(MAKE) -C mobile/xul/installer elfhack
-    else
-		$(MAKE) -C $(MOZ_BUILD_APP)/installer elfhack
-    endif
-endif
 	echo building symbol store
 	$(RM) -r $(DIST)/crashreporter-symbols
 	$(RM) "$(DIST)/$(SYMBOL_ARCHIVE_BASENAME).zip"
 	$(NSINSTALL) -D $(DIST)/crashreporter-symbols
 	OBJCOPY="$(OBJCOPY)" \
 	$(PYTHON) $(topsrcdir)/toolkit/crashreporter/tools/symbolstore.py \
 	  $(MAKE_SYM_STORE_ARGS)                                          \
 	  $(foreach dir,$(SYM_STORE_SOURCE_DIRS),-s $(dir))               \
--- a/accessible/public/nsIAccessibleProvider.idl
+++ b/accessible/public/nsIAccessibleProvider.idl
@@ -57,18 +57,18 @@ interface nsIAccessibleProvider : nsISup
   const long XULStatusBar = 0x00001014;
   const long XULRadioButton = 0x00001015;
   const long XULRadioGroup = 0x00001016;
 
   /** Used for XUL tab element */
   const long XULTab = 0x00001017;
   /** Used for XUL tabs element, a container for tab elements */
   const long XULTabs = 0x00001018;
-  /** Used for XUL deck frame */
-  const long XULDeck = 0x00001019;
+  /** Used for XUL tabpanels element */
+  const long XULTabpanels = 0x00001019;
 
   const long XULText             = 0x0000101A;
   const long XULTextBox          = 0x0000101B;
   const long XULThumb            = 0x0000101C;
   const long XULTree             = 0x0000101D;
   const long XULTreeColumns      = 0x0000101E;
   const long XULTreeColumnItem   = 0x0000101F;
   const long XULToolbar          = 0x00001020;
--- a/accessible/src/base/AccEvent.cpp
+++ b/accessible/src/base/AccEvent.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set 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/. */
 
 #include "AccEvent.h"
 
 #include "ApplicationAccessibleWrap.h"
 #include "nsAccessibilityService.h"
@@ -112,20 +113,22 @@ AccTextChangeEvent::CreateXPCOMObject()
 // AccReorderEvent
 ////////////////////////////////////////////////////////////////////////////////
 
 uint32_t
 AccReorderEvent::IsShowHideEventTarget(const Accessible* aTarget) const
 {
   uint32_t count = mDependentEvents.Length();
   for (uint32_t index = count - 1; index < count; index--) {
-    if (mDependentEvents[index]->mAccessible == aTarget &&
-        mDependentEvents[index]->mEventType == nsIAccessibleEvent::EVENT_SHOW ||
-        mDependentEvents[index]->mEventType == nsIAccessibleEvent::EVENT_HIDE) {
-      return mDependentEvents[index]->mEventType;
+    if (mDependentEvents[index]->mAccessible == aTarget) {
+      uint32_t eventType = mDependentEvents[index]->mEventType;
+      if (eventType == nsIAccessibleEvent::EVENT_SHOW ||
+          eventType == nsIAccessibleEvent::EVENT_HIDE) {
+        return mDependentEvents[index]->mEventType;
+      }
     }
   }
 
   return 0;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // AccHideEvent
--- a/accessible/src/base/AccTypes.h
+++ b/accessible/src/base/AccTypes.h
@@ -47,17 +47,17 @@ enum AccType {
   /**
    * Other accessible types.
    */
   eApplicationType,
   eImageMapType,
   eMenuPopupType,
   eProgressType,
   eRootType,
-  eXULDeckType,
+  eXULTabpanelsType,
   eXULTreeType,
 
   eLastAccType = eXULTreeType
 };
 
 /**
  * Generic accessible type, different accessible classes can share the same
  * type, the same accessible class can have several types.
--- a/accessible/src/base/DocManager.cpp
+++ b/accessible/src/base/DocManager.cpp
@@ -44,17 +44,17 @@ DocAccessible*
 DocManager::GetDocAccessible(nsIDocument* aDocument)
 {
   if (!aDocument)
     return nullptr;
 
   // Ensure CacheChildren is called before we query cache.
   ApplicationAcc()->EnsureChildren();
 
-  DocAccessible* docAcc = mDocAccessibleCache.GetWeak(aDocument);
+  DocAccessible* docAcc = GetExistingDocAccessible(aDocument);
   if (docAcc)
     return docAcc;
 
   return CreateDocOrRootAccessible(aDocument);
 }
 
 Accessible*
 DocManager::FindAccessibleInCache(nsINode* aNode) const
@@ -176,17 +176,17 @@ DocManager::OnStateChange(nsIWebProgress
   }
 
   // Document loading was started.
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eDocLoad))
     logging::DocLoad("start document loading", aWebProgress, aRequest, aStateFlags);
 #endif
 
-  DocAccessible* docAcc = mDocAccessibleCache.GetWeak(document);
+  DocAccessible* docAcc = GetExistingDocAccessible(document);
   if (!docAcc)
     return NS_OK;
 
   nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(DOMWindow));
   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));
   NS_ENSURE_STATE(docShell);
 
   bool isReloading = false;
@@ -275,17 +275,17 @@ DocManager::HandleEvent(nsIDOMEvent* aEv
     if (document->IsInitialDocument())
       return NS_OK;
 
     // Shutdown this one and sub document accessibles.
 
     // We're allowed to not remove listeners when accessible document is
     // shutdown since we don't keep strong reference on chrome event target and
     // listeners are removed automatically when chrome event target goes away.
-    DocAccessible* docAccessible = mDocAccessibleCache.GetWeak(document);
+    DocAccessible* docAccessible = GetExistingDocAccessible(document);
     if (docAccessible)
       docAccessible->Shutdown();
 
     return NS_OK;
   }
 
   // XXX: handle error pages loading separately since they get neither
   // webprogress notifications nor 'pageshow' event.
@@ -307,17 +307,17 @@ DocManager::HandleEvent(nsIDOMEvent* aEv
 // DocManager private
 
 void
 DocManager::HandleDOMDocumentLoad(nsIDocument* aDocument,
                                   uint32_t aLoadEventType)
 {
   // Document accessible can be created before we were notified the DOM document
   // was loaded completely. However if it's not created yet then create it.
-  DocAccessible* docAcc = mDocAccessibleCache.GetWeak(aDocument);
+  DocAccessible* docAcc = GetExistingDocAccessible(aDocument);
   if (!docAcc) {
     docAcc = CreateDocOrRootAccessible(aDocument);
     if (!docAcc)
       return;
   }
 
   docAcc->NotifyOfLoad(aLoadEventType);
 }
@@ -353,17 +353,17 @@ DocManager::CreateDocOrRootAccessible(ns
   // Ignore temporary, hiding, resource documents and documents without
   // docshell.
   if (aDocument->IsInitialDocument() || !aDocument->IsVisible() ||
       aDocument->IsResourceDoc() || !aDocument->IsActive())
     return nullptr;
 
   // Ignore documents without presshell and not having root frame.
   nsIPresShell* presShell = aDocument->GetShell();
-  if (!presShell || !presShell->GetRootFrame() || presShell->IsDestroying())
+  if (!presShell || presShell->IsDestroying())
     return nullptr;
 
   bool isRootDoc = nsCoreUtils::IsRootDocument(aDocument);
 
   DocAccessible* parentDocAcc = nullptr;
   if (!isRootDoc) {
     // XXXaaronl: ideally we would traverse the presshell chain. Since there's
     // no easy way to do that, we cheat and use the document hierarchy.
--- a/accessible/src/base/DocManager.h
+++ b/accessible/src/base/DocManager.h
@@ -55,24 +55,16 @@ public:
 
   /**
    * Search through all document accessibles for an accessible with the given
    * unique id.
    */
   Accessible* FindAccessibleInCache(nsINode* aNode) const;
 
   /**
-   * Return document accessible from the cache. Convenient method for testing.
-   */
-  inline DocAccessible* GetDocAccessibleFromCache(nsIDocument* aDocument) const
-  {
-    return mDocAccessibleCache.GetWeak(aDocument);
-  }
-
-  /**
    * Called by document accessible when it gets shutdown.
    */
   inline void NotifyOfDocumentShutdown(nsIDocument* aDocument)
   {
     mDocAccessibleCache.Remove(aDocument);
   }
 
 #ifdef DEBUG
@@ -149,12 +141,24 @@ private:
   static PLDHashOperator
     SearchIfDocIsRefreshing(const nsIDocument* aKey,
                             DocAccessible* aDocAccessible, void* aUserArg);
 #endif
 
   DocAccessibleHashtable mDocAccessibleCache;
 };
 
+/**
+ * Return the existing document accessible for the document if any.
+ * Note this returns the doc accessible for the primary pres shell if there is
+ * more than one.
+ */
+inline DocAccessible*
+GetExistingDocAccessible(const nsIDocument* aDocument)
+{
+  nsIPresShell* ps = aDocument->GetShell();
+  return ps ? ps->GetDocAccessible() : nullptr;
+}
+
 } // namespace a11y
 } // namespace mozilla
 
 #endif // mozilla_a11_DocManager_h_
--- a/accessible/src/base/Logging.cpp
+++ b/accessible/src/base/Logging.cpp
@@ -395,18 +395,17 @@ logging::DocLoad(const char* aMsg, nsIWe
   nsCOMPtr<nsIDOMDocument> DOMDocument;
   DOMWindow->GetDocument(getter_AddRefs(DOMDocument));
   if (!DOMDocument) {
     MsgEnd();
     return;
   }
 
   nsCOMPtr<nsIDocument> documentNode(do_QueryInterface(DOMDocument));
-  DocAccessible* document =
-    GetAccService()->GetDocAccessibleFromCache(documentNode);
+  DocAccessible* document = GetExistingDocAccessible(documentNode);
 
   LogDocInfo(documentNode, document);
 
   nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(DOMWindow));
   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));
   printf("\n    ");
   LogShellLoadType(docShell);
   printf("\n");
@@ -420,18 +419,17 @@ logging::DocLoad(const char* aMsg, nsIWe
   MsgEnd();
 }
 
 void
 logging::DocLoad(const char* aMsg, nsIDocument* aDocumentNode)
 {
   MsgBegin(sDocLoadTitle, aMsg);
 
-  DocAccessible* document =
-    GetAccService()->GetDocAccessibleFromCache(aDocumentNode);
+  DocAccessible* document = GetExistingDocAccessible(aDocumentNode);
   LogDocInfo(aDocumentNode, document);
 
   MsgEnd();
 }
 
 void
 logging::DocCompleteLoad(DocAccessible* aDocument, bool aIsLoadEventTarget)
 {
@@ -481,29 +479,29 @@ logging::DocLoadEventHandled(AccEvent* a
   MsgEnd();
 }
 
 void
 logging::DocCreate(const char* aMsg, nsIDocument* aDocumentNode,
                    DocAccessible* aDocument)
 {
   DocAccessible* document = aDocument ?
-    aDocument : GetAccService()->GetDocAccessibleFromCache(aDocumentNode);
+    aDocument : GetExistingDocAccessible(aDocumentNode);
 
   MsgBegin(sDocCreateTitle, aMsg);
   LogDocInfo(aDocumentNode, document);
   MsgEnd();
 }
 
 void
 logging::DocDestroy(const char* aMsg, nsIDocument* aDocumentNode,
                     DocAccessible* aDocument)
 {
   DocAccessible* document = aDocument ?
-    aDocument : GetAccService()->GetDocAccessibleFromCache(aDocumentNode);
+    aDocument : GetExistingDocAccessible(aDocumentNode);
 
   MsgBegin(sDocDestroyTitle, aMsg);
   LogDocInfo(aDocumentNode, document);
   MsgEnd();
 }
 
 void
 logging::OuterDocDestroy(OuterDocAccessible* aOuterDoc)
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "NotificationController.h"
 
 #include "Accessible-inl.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
-#include "DocAccessible.h"
+#include "DocAccessible-inl.h"
 #include "nsEventShell.h"
 #include "FocusManager.h"
 #include "Role.h"
 #include "TextLeafAccessible.h"
 #include "TextUpdater.h"
 
 #ifdef A11Y_LOG
 #include "Logging.h"
@@ -208,16 +208,20 @@ NotificationController::WillRefresh(mozi
 #endif
 
     mDocument->DoInitialUpdate();
 
     NS_ASSERTION(mContentInsertions.Length() == 0,
                  "Pending content insertions while initial accessible tree isn't created!");
   }
 
+  // Initialize scroll support if needed.
+  if (!(mDocument->mDocFlags & DocAccessible::eScrollInitialized))
+    mDocument->AddScrollListener();
+
   // Process content inserted notifications to update the tree. Process other
   // notifications like DOM events and then flush event queue. If any new
   // notifications are queued during this processing then they will be processed
   // on next refresh. If notification processing queues up new events then they
   // are processed in this refresh. If events processing queues up new events
   // then new events are processed on next refresh.
   // Note: notification processing or event handling may shut down the owning
   // document accessible.
@@ -632,17 +636,16 @@ NotificationController::CoalesceTextChan
   }
 
   aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
 }
 
 void
 NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent)
 {
-  DocAccessible* document = aEvent->GetDocAccessible();
   Accessible* container = aEvent->mAccessible->Parent();
   if (!container)
     return;
 
   HyperTextAccessible* textAccessible = container->AsHyperText();
   if (!textAccessible)
     return;
 
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -244,17 +244,17 @@ static nsRoleMapEntry sWAIRoleMaps[] =
   },
   { // listbox
     &nsGkAtoms::listbox,
     roles::LISTBOX,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
-    eSelect,
+    eListControl | eSelect,
     kNoReqStates,
     eARIAMultiSelectable,
     eARIAReadonly
   },
   { // listitem
     &nsGkAtoms::listitem,
     roles::LISTITEM,
     kUseMapRole,
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -55,16 +55,17 @@
 #include "nsIObserverService.h"
 #include "nsLayoutUtils.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsObjectFrame.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/Util.h"
+#include "nsDeckFrame.h"
 
 #ifdef MOZ_XUL
 #include "XULAlertAccessible.h"
 #include "XULColorPickerAccessible.h"
 #include "XULComboboxAccessible.h"
 #include "XULElementAccessibles.h"
 #include "XULFormControlAccessible.h"
 #include "XULListboxAccessibleWrap.h"
@@ -180,32 +181,32 @@ nsAccessibilityService::FireAccessibleEv
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessibilityService
 
 Accessible*
 nsAccessibilityService::GetRootDocumentAccessible(nsIPresShell* aPresShell,
                                                   bool aCanCreate)
 {
+  nsIPresShell* ps = aPresShell;
   nsIDocument* documentNode = aPresShell->GetDocument();
   if (documentNode) {
     nsCOMPtr<nsISupports> container = documentNode->GetContainer();
     nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(container));
     if (treeItem) {
       nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
       treeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem));
       if (treeItem != rootTreeItem) {
         nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(rootTreeItem));
         nsCOMPtr<nsIPresShell> presShell;
         docShell->GetPresShell(getter_AddRefs(presShell));
-        documentNode = presShell->GetDocument();
+        ps = presShell;
       }
 
-      return aCanCreate ?
-        GetDocAccessible(documentNode) : GetDocAccessibleFromCache(documentNode);
+      return aCanCreate ? GetDocAccessible(ps) : ps->GetDocAccessible();
     }
   }
   return nullptr;
 }
 
 already_AddRefed<Accessible>
 nsAccessibilityService::CreatePluginAccessible(nsObjectFrame* aFrame,
                                                nsIContent* aContent,
@@ -250,16 +251,57 @@ nsAccessibilityService::CreatePluginAcce
 #endif
   }
 #endif
 
   return nullptr;
 }
 
 void
+nsAccessibilityService::DeckPanelSwitched(nsIPresShell* aPresShell,
+                                          nsIContent* aDeckNode,
+                                          nsIFrame* aPrevBoxFrame,
+                                          nsIFrame* aCurrentBoxFrame)
+{
+  // Ignore tabpanels elements (a deck having an accessible) since their
+  // children are accessible not depending on selected tab.
+  DocAccessible* document = GetDocAccessible(aPresShell);
+  if (!document || document->HasAccessible(aDeckNode))
+    return;
+
+  if (aPrevBoxFrame) {
+    nsIContent* panelNode = aPrevBoxFrame->GetContent();
+#ifdef A11Y_LOG
+    if (logging::IsEnabled(logging::eTree)) {
+      logging::MsgBegin("TREE", "deck panel unselected");
+      logging::Node("container", panelNode);
+      logging::Node("content", aDeckNode);
+      logging::MsgEnd();
+    }
+#endif
+
+    document->ContentRemoved(aDeckNode, panelNode);
+  }
+
+  if (aCurrentBoxFrame) {
+    nsIContent* panelNode = aCurrentBoxFrame->GetContent();
+#ifdef A11Y_LOG
+    if (logging::IsEnabled(logging::eTree)) {
+      logging::MsgBegin("TREE", "deck panel selected");
+      logging::Node("container", panelNode);
+      logging::Node("content", aDeckNode);
+      logging::MsgEnd();
+    }
+#endif
+
+    document->ContentInserted(aDeckNode, panelNode, panelNode->GetNextSibling());
+  }
+}
+
+void
 nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell,
                                              nsIContent* aContainer,
                                              nsIContent* aStartChild,
                                              nsIContent* aEndChild)
 {
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eTree)) {
     logging::MsgBegin("TREE", "content inserted");
@@ -359,25 +401,22 @@ nsAccessibilityService::UpdateImageMap(n
       RecreateAccessible(presShell, aImageFrame->GetContent());
     }
   }
 }
 
 void
 nsAccessibilityService::PresShellActivated(nsIPresShell* aPresShell)
 {
-  nsIDocument* DOMDoc = aPresShell->GetDocument();
-  if (DOMDoc) {
-    DocAccessible* document = GetDocAccessibleFromCache(DOMDoc);
-    if (document) {
-      RootAccessible* rootDocument = document->RootAccessible();
-      NS_ASSERTION(rootDocument, "Entirely broken tree: no root document!");
-      if (rootDocument)
-        rootDocument->DocumentActivated(document);
-    }
+  DocAccessible* document = aPresShell->GetDocAccessible();
+  if (document) {
+    RootAccessible* rootDocument = document->RootAccessible();
+    NS_ASSERTION(rootDocument, "Entirely broken tree: no root document!");
+    if (rootDocument)
+      rootDocument->DocumentActivated(document);
   }
 }
 
 void
 nsAccessibilityService::RecreateAccessible(nsIPresShell* aPresShell,
                                            nsIContent* aContent)
 {
   DocAccessible* document = GetDocAccessible(aPresShell);
@@ -603,17 +642,17 @@ nsAccessibilityService::GetAccessibleFro
   // our cache of document accessibles (document cache). Note usually shutdown
   // document accessibles are not stored in the document cache, however an
   // "unofficially" shutdown document (i.e. not from DocManager) can still
   // exist in the document cache.
   Accessible* accessible = FindAccessibleInCache(node);
   if (!accessible) {
     nsCOMPtr<nsIDocument> document(do_QueryInterface(node));
     if (document)
-      accessible = GetDocAccessibleFromCache(document);
+      accessible = GetExistingDocAccessible(document);
   }
 
   NS_IF_ADDREF(*aAccessible = accessible);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAccessibilityService::CreateAccessiblePivot(nsIAccessible* aRoot,
@@ -799,17 +838,17 @@ nsAccessibilityService::GetOrCreateAcces
       // on accessible HTML table elements.
       if ((roleMapEntry->accTypes & eTableCell)) {
         if (aContext->IsTableRow() &&
             (frame->AccessibleType() != eHTMLTableCellType ||
              aContext->GetContent() != content->GetParent())) {
           newAcc = new ARIAGridCellAccessibleWrap(content, document);
         }
 
-      } else if ((roleMapEntry->accTypes & eTable) &&
+      } else if ((roleMapEntry->IsOfType(eTable)) &&
                  frame->AccessibleType() != eHTMLTableType) {
         newAcc = new ARIAGridAccessibleWrap(content, document);
       }
     }
 
     if (!newAcc) {
       // Prefer to use markup (mostly tag name, perhaps attributes) to decide if
       // and what kind of accessible to create.
@@ -819,57 +858,72 @@ nsAccessibilityService::GetOrCreateAcces
       if (!newAcc)
         newAcc = CreateAccessibleByFrameType(frame, content, aContext);
 
       // If table has strong ARIA role then all table descendants shouldn't
       // expose their native roles.
       if (!roleMapEntry && newAcc) {
         if (frame->AccessibleType() == eHTMLTableRowType) {
           nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
-          if (contextRoleMap && !(contextRoleMap->accTypes & eTable))
+          if (contextRoleMap && !(contextRoleMap->IsOfType(eTable)))
             roleMapEntry = &nsARIAMap::gEmptyRoleMap;
 
         } else if (frame->AccessibleType() == eHTMLTableCellType &&
                    aContext->ARIARoleMap() == &nsARIAMap::gEmptyRoleMap) {
           roleMapEntry = &nsARIAMap::gEmptyRoleMap;
 
         } else if (content->Tag() == nsGkAtoms::dt ||
                    content->Tag() == nsGkAtoms::li ||
                    content->Tag() == nsGkAtoms::dd ||
                    frame->AccessibleType() == eHTMLLiType) {
           nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
-          if (contextRoleMap && !(contextRoleMap->accTypes & eList))
+          if (contextRoleMap && !(contextRoleMap->IsOfType(eList)))
             roleMapEntry = &nsARIAMap::gEmptyRoleMap;
         }
       }
     }
   }
 
-  if (!newAcc) {
+  // Accessible XBL types and deck stuff are used in XUL only currently.
+  if (!newAcc && content->IsXUL()) {
+    // No accessible for not selected deck panel and its children.
+    if (!aContext->IsXULTabpanels()) {
+      nsDeckFrame* deckFrame = do_QueryFrame(frame->GetParent());
+      if (deckFrame && deckFrame->GetSelectedBox() != frame) {
+        if (aIsSubtreeHidden)
+          *aIsSubtreeHidden = true;
+
+        return nullptr;
+      }
+    }
+
     // Elements may implement nsIAccessibleProvider via XBL. This allows them to
     // say what kind of accessible to create.
     newAcc = CreateAccessibleByType(content, document);
+
+    // Any XUL box can be used as tabpanel, make sure we create a proper
+    // accessible for it.
+    if (!newAcc && aContext->IsXULTabpanels() &&
+        content->GetParent() == aContext->GetContent()) {
+      nsIAtom* frameType = frame->GetType();
+      if (frameType == nsGkAtoms::boxFrame ||
+          frameType == nsGkAtoms::scrollFrame) {
+        newAcc = new XULTabpanelAccessible(content, document);
+      }
+    }
   }
 
   if (!newAcc) {
-    // xul:deck does not have XBL and nsIFrame::CreateAccessible() is only called 
-    // on HTML elements
-    nsIAtom* tag = content->Tag();
-    if ((tag == nsGkAtoms::deck) || (tag == nsGkAtoms::tabpanels)) {
-      newAcc = new XULDeckAccessible(content, document);
-    } else if (content->IsSVG(nsGkAtoms::svg)) {
+    if (content->IsSVG(nsGkAtoms::svg)) {
       newAcc = new EnumRoleAccessible(content, document, roles::DIAGRAM);
     } else if (content->IsMathML(nsGkAtoms::math)) {
       newAcc = new EnumRoleAccessible(content, document, roles::EQUATION);
     }
   }
 
-  if (!newAcc)
-    newAcc = CreateAccessibleForDeckChild(frame, content, document);
-
   // If no accessible, see if we need to create a generic accessible because
   // of some property that makes this object interesting
   // We don't do this for <body>, <html>, <window>, <dialog> etc. which
   // correspond to the doc accessible and will be created in any case
   if (!newAcc && content->Tag() != nsGkAtoms::body && content->GetParent() &&
       (roleMapEntry || MustBeAccessible(content, document) ||
        (isHTML && nsCoreUtils::HasClickListener(content)))) {
     // This content is focusable or has an interesting dynamic content accessibility property.
@@ -1006,18 +1060,18 @@ nsAccessibilityService::CreateAccessible
     case nsIAccessibleProvider::XULColorPickerTile:
       accessible = new XULColorPickerTileAccessible(aContent, aDoc);
       break;
 
     case nsIAccessibleProvider::XULCombobox:
       accessible = new XULComboboxAccessible(aContent, aDoc);
       break;
 
-    case nsIAccessibleProvider::XULDeck:
-      accessible = new XULDeckAccessible(aContent, aDoc);
+    case nsIAccessibleProvider::XULTabpanels:
+      accessible = new XULTabpanelsAccessible(aContent, aDoc);
       break;
 
     case nsIAccessibleProvider::XULDropmarker:
       accessible = new XULDropmarkerAccessible(aContent, aDoc);
       break;
 
     case nsIAccessibleProvider::XULGroupbox:
       accessible = new XULGroupboxAccessible(aContent, aDoc);
@@ -1401,16 +1455,19 @@ nsAccessibilityService::CreateAccessible
     case ePluginType: {
       nsObjectFrame* objectFrame = do_QueryFrame(aFrame);
       newAcc = CreatePluginAccessible(objectFrame, aContent, aContext);
       break;
     }
     case eTextLeafType:
       newAcc = new TextLeafAccessibleWrap(aContent, document);
       break;
+    default:
+      MOZ_ASSERT(false);
+      break;
   }
 
   return newAcc.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessibilityService (DON'T put methods here)
 
@@ -1475,47 +1532,16 @@ NS_GetAccessibilityService(nsIAccessibil
   NS_ADDREF(*aResult = service);
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessibilityService private (DON'T put methods here)
 
-already_AddRefed<Accessible>
-nsAccessibilityService::CreateAccessibleForDeckChild(nsIFrame* aFrame,
-                                                     nsIContent* aContent,
-                                                     DocAccessible* aDoc)
-{
-  if (aFrame->GetType() == nsGkAtoms::boxFrame ||
-      aFrame->GetType() == nsGkAtoms::scrollFrame) {
-
-    nsIFrame* parentFrame = aFrame->GetParent();
-    if (parentFrame && parentFrame->GetType() == nsGkAtoms::deckFrame) {
-      // If deck frame is for xul:tabpanels element then the given node has
-      // tabpanel accessible.
-      nsIContent* parentContent = parentFrame->GetContent();
-#ifdef MOZ_XUL
-      if (parentContent->NodeInfo()->Equals(nsGkAtoms::tabpanels,
-                                            kNameSpaceID_XUL)) {
-        Accessible* accessible = new XULTabpanelAccessible(aContent, aDoc);
-        NS_ADDREF(accessible);
-        return accessible;
-      }
-#endif
-      Accessible* accessible = new EnumRoleAccessible(aContent, aDoc,
-                                                      roles::PROPERTYPAGE);
-      NS_ADDREF(accessible);
-      return accessible;
-    }
-  }
-
-  return nullptr;
-}
-
 #ifdef MOZ_XUL
 already_AddRefed<Accessible>
 nsAccessibilityService::CreateAccessibleForXULTree(nsIContent* aContent,
                                                    DocAccessible* aDoc)
 {
   nsCOMPtr<nsITreeBoxObject> treeBoxObj = nsCoreUtils::GetTreeBoxObject(aContent);
   if (!treeBoxObj)
     return nullptr;
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -60,16 +60,23 @@ public:
   /**
    * Adds/remove ATK root accessible for gtk+ native window to/from children
    * of the application accessible.
    */
   virtual Accessible* AddNativeRootAccessible(void* aAtkAccessible);
   virtual void RemoveNativeRootAccessible(Accessible* aRootAccessible);
 
   /**
+   * Notification used to update the accessible tree when deck panel is
+   * switched.
+   */
+  void DeckPanelSwitched(nsIPresShell* aPresShell, nsIContent* aDeckNode,
+                         nsIFrame* aPrevBoxFrame, nsIFrame* aCurrentBoxFrame);
+
+  /**
    * Notification used to update the accessible tree when new content is
    * inserted.
    */
   void ContentRangeInserted(nsIPresShell* aPresShell, nsIContent* aContainer,
                             nsIContent* aStartChild, nsIContent* aEndChild);
 
   /**
    * Notification used to update the accessible tree when content is removed.
@@ -168,23 +175,16 @@ private:
 
   /**
    * Create an accessible whose type depends on the given frame.
    */
   already_AddRefed<Accessible>
     CreateAccessibleByFrameType(nsIFrame* aFrame, nsIContent* aContent,
                                 Accessible* aContext);
 
-  /**
-   * Create accessible if parent is a deck frame.
-   */
-  already_AddRefed<Accessible>
-    CreateAccessibleForDeckChild(nsIFrame* aFrame, nsIContent* aContent,
-                                 DocAccessible* aDoc);
-
 #ifdef MOZ_XUL
   /**
    * Create accessible for XUL tree element.
    */
   already_AddRefed<Accessible>
     CreateAccessibleForXULTree(nsIContent* aContent, DocAccessible* aDoc);
 #endif
 
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -259,16 +259,26 @@ Accessible::Name(nsString& aName)
       aName.CompressWhitespace();
       return eNameFromTooltip;
     }
   } else if (mContent->IsXUL()) {
     if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
       aName.CompressWhitespace();
       return eNameFromTooltip;
     }
+  } else if (mContent->IsSVG()) {
+    // If user agents need to choose among multiple ‘desc’ or ‘title’ elements
+    // for processing, the user agent shall choose the first one.
+    for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
+         childElm = childElm->GetNextSibling()) {
+      if (childElm->IsSVG(nsGkAtoms::title)) {
+        nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
+        return eNameFromTooltip;
+      }
+    }
   }
 
   if (nameFlag != eNoNameOnPurpose)
     aName.SetIsVoid(true);
 
   return nameFlag;
 }
 
@@ -302,35 +312,50 @@ Accessible::Description(nsString& aDescr
                            aDescription);
 
   if (aDescription.IsEmpty()) {
     bool isXUL = mContent->IsXUL();
     if (isXUL) {
       // Try XUL <description control="[id]">description text</description>
       XULDescriptionIterator iter(Document(), mContent);
       Accessible* descr = nullptr;
-      while ((descr = iter.Next()))
+      while ((descr = iter.Next())) {
         nsTextEquivUtils::AppendTextEquivFromContent(this, descr->GetContent(),
                                                      &aDescription);
       }
-
-      if (aDescription.IsEmpty()) {
-        nsIAtom *descAtom = isXUL ? nsGkAtoms::tooltiptext :
-                                    nsGkAtoms::title;
-        if (mContent->GetAttr(kNameSpaceID_None, descAtom, aDescription)) {
-          nsAutoString name;
-          Name(name);
-          if (name.IsEmpty() || aDescription == name)
-            // Don't use tooltip for a description if this object
-            // has no name or the tooltip is the same as the name
-            aDescription.Truncate();
+    }
+
+    if (aDescription.IsEmpty()) {
+      // Keep the Name() method logic.
+      if (mContent->IsHTML()) {
+        mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aDescription);
+      } else if (mContent->IsXUL()) {
+        mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aDescription);
+      } else if (mContent->IsSVG()) {
+        for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
+             childElm = childElm->GetNextSibling()) {
+          if (childElm->IsSVG(nsGkAtoms::title)) {
+            nsTextEquivUtils::AppendTextEquivFromContent(this, childElm,
+                                                         &aDescription);
+            break;
+          }
         }
       }
+
+      if (!aDescription.IsEmpty()) {
+        nsAutoString name;
+        ENameValueFlag nameFlag = Name(name);
+
+        // Don't use tooltip for a description if it was used for a name.
+        if (nameFlag == eNameFromTooltip)
+          aDescription.Truncate();
+      }
     }
-    aDescription.CompressWhitespace();
+  }
+  aDescription.CompressWhitespace();
 }
 
 NS_IMETHODIMP
 Accessible::GetAccessKey(nsAString& aAccessKey)
 {
   aAccessKey.Truncate();
 
   if (IsDefunct())
@@ -607,16 +632,17 @@ Accessible::VisibilityState()
     // deck panel.
     nsIFrame* parentFrame = curFrame->GetParent();
     nsDeckFrame* deckFrame = do_QueryFrame(parentFrame);
     if (deckFrame && deckFrame->GetSelectedBox() != curFrame) {
       if (deckFrame->GetContent()->IsXUL() &&
           deckFrame->GetContent()->Tag() == nsGkAtoms::tabpanels)
         return states::OFFSCREEN;
 
+      NS_NOTREACHED("Children of not selected deck panel are not accessible.");
       return states::INVISIBLE;
     }
 
     // If contained by scrollable frame then check that at least 12 pixels
     // around the object is visible, otherwise the object is offscreen.
     framePos += curFrame->GetPosition();
     nsIScrollableFrame* scrollableFrame = do_QueryFrame(parentFrame);
     if (scrollableFrame) {
@@ -1584,27 +1610,54 @@ Accessible::GetValue(nsAString& aValue)
   aValue.Assign(value);
 
   return NS_OK;
 }
 
 void
 Accessible::Value(nsString& aValue)
 {
-  if (mRoleMapEntry) {
-    if (mRoleMapEntry->valueRule == eNoValue)
-      return;
-
-    // aria-valuenow is a number, and aria-valuetext is the optional text equivalent
-    // For the string value, we will try the optional text equivalent first
+  if (!mRoleMapEntry)
+    return;
+
+  if (mRoleMapEntry->valueRule != eNoValue) {
+    // aria-valuenow is a number, and aria-valuetext is the optional text
+    // equivalent. For the string value, we will try the optional text
+    // equivalent first.
     if (!mContent->GetAttr(kNameSpaceID_None,
                            nsGkAtoms::aria_valuetext, aValue)) {
       mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow,
                         aValue);
     }
+    return;
+  }
+
+  // Value of combobox is a text of current or selected item.
+  if (mRoleMapEntry->Is(nsGkAtoms::combobox)) {
+    Accessible* option = CurrentItem();
+    if (!option) {
+      Accessible* listbox = nullptr;
+      IDRefsIterator iter(mDoc, mContent, nsGkAtoms::aria_owns);
+      while ((listbox = iter.Next()) && !listbox->IsListControl());
+
+      if (!listbox) {
+        uint32_t childCount = ChildCount();
+        for (uint32_t idx = 0; idx < childCount; idx++) {
+          Accessible* child = mChildren.ElementAt(idx);
+          if (child->IsListControl())
+            listbox = child;
+        }
+      }
+
+      if (listbox)
+        option = listbox->GetSelectedItem(0);
+    }
+
+    if (option)
+      nsTextEquivUtils::GetNameFromSubtree(option, aValue);
   }
 }
 
 // nsIAccessibleValue
 NS_IMETHODIMP
 Accessible::GetMaximumValue(double *aMaximumValue)
 {
   return GetAttrValue(nsGkAtoms::aria_valuemax, aMaximumValue);
@@ -2461,16 +2514,28 @@ ENameValueFlag
 Accessible::NativeName(nsString& aName)
 {
   if (mContent->IsHTML())
     return GetHTMLName(aName);
 
   if (mContent->IsXUL())
     return GetXULName(aName);
 
+  if (mContent->IsSVG()) {
+    // If user agents need to choose among multiple ‘desc’ or ‘title’ elements
+    // for processing, the user agent shall choose the first one.
+    for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
+         childElm = childElm->GetNextSibling()) {
+      if (childElm->IsSVG(nsGkAtoms::desc)) {
+        nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
+        return eNameOK;
+      }
+    }
+  }
+
   return eNameOK;
 }
 
 // Accessible protected
 void
 Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent)
 {
   NS_PRECONDITION(aParent, "This method isn't used to set null parent!");
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -515,17 +515,17 @@ public:
 
   virtual TableCellAccessible* AsTableCell() { return nullptr; }
 
   bool IsTableRow() const { return HasGenericType(eTableRow); }
 
   bool IsTextLeaf() const { return mType == eTextLeafType; }
   TextLeafAccessible* AsTextLeaf();
 
-  bool IsXULDeck() const { return mType == eXULDeckType; }
+  bool IsXULTabpanels() const { return mType == eXULTabpanelsType; }
 
   bool IsXULTree() const { return mType == eXULTreeType; }
   XULTreeAccessible* AsXULTree();
 
   /**
    * Return true if the accessible belongs to the given accessible type.
    */
   bool HasGenericType(AccGenericType aType) const;
--- a/accessible/src/generic/DocAccessible-inl.h
+++ b/accessible/src/generic/DocAccessible-inl.h
@@ -61,16 +61,43 @@ DocAccessible::UpdateText(nsIContent* aT
   NS_ASSERTION(mNotificationController, "The document was shut down!");
 
   // Ignore the notification if initial tree construction hasn't been done yet.
   if (mNotificationController && HasLoadState(eTreeConstructed))
     mNotificationController->ScheduleTextUpdate(aTextNode);
 }
 
 inline void
+DocAccessible::AddScrollListener()
+{
+  // Delay scroll initializing until the document has a root frame.
+  if (!mPresShell->GetRootFrame())
+    return;
+
+  mDocFlags |= eScrollInitialized;
+  nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollable();
+  if (sf) {
+    sf->AddScrollPositionListener(this);
+
+#ifdef A11Y_LOG
+    if (logging::IsEnabled(logging::eDocCreate))
+      logging::Text("add scroll listener");
+#endif
+  }
+}
+
+inline void
+DocAccessible::RemoveScrollListener()
+{
+  nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollable();
+  if (sf)
+    sf->RemoveScrollPositionListener(this);
+}
+
+inline void
 DocAccessible::NotifyOfLoad(uint32_t aLoadEventType)
 {
   mLoadState |= eDOMLoaded;
   mLoadEventType = aLoadEventType;
 
   // If the document is loaded completely then network activity was presumingly
   // caused by file loading. Fire busy state change event.
   if (HasLoadState(eCompletelyLoaded) && IsLoadEventTarget()) {
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -70,17 +70,17 @@ static const uint32_t kRelationAttrsLen 
 ////////////////////////////////////////////////////////////////////////////////
 // Constructor/desctructor
 
 DocAccessible::
   DocAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
                   nsIPresShell* aPresShell) :
   HyperTextAccessibleWrap(aRootContent, this),
   mDocumentNode(aDocument), mScrollPositionChangedTicks(0),
-  mLoadState(eTreeConstructionPending), mLoadEventType(0),
+  mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
   mVirtualCursor(nullptr),
   mPresShell(aPresShell)
 {
   mGenericTypes |= eDocument;
   mStateFlags |= eNotNodeMapEntry;
 
   MOZ_ASSERT(mPresShell, "should have been given a pres shell");
   mPresShell->SetDocAccessible(this);
@@ -88,28 +88,16 @@ DocAccessible::
   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 (mDocumentNode && mDocumentNode->IsXUL())
     mGenericTypes &= ~eHyperText;
-
-  // For GTK+ native window, we do nothing here.
-  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 = (!(mDocumentNode->GetParentDocument()) ||
-                   nsCoreUtils::IsTabDocument(mDocumentNode));
 }
 
 DocAccessible::~DocAccessible()
 {
   NS_ASSERTION(!mPresShell, "LastRelease was never called!?!");
 }
 
 
@@ -140,17 +128,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
   NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
   NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivotObserver)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleDocument)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleCursorable,
-                                     mIsCursorable)
+                                     (mDocFlags & eCursorable))
     foundInterface = 0;
 
   nsresult status;
   if (!foundInterface) {
     // HTML document accessible must inherit from HyperTextAccessible to get
     // support text interfaces. XUL document accessible doesn't need this.
     // However at some point we may push <body> to implement the interfaces and
     // return DocAccessible to inherit from AccessibleWrap.
@@ -497,17 +485,18 @@ NS_IMETHODIMP
 DocAccessible::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor)
 {
   NS_ENSURE_ARG_POINTER(aVirtualCursor);
   *aVirtualCursor = nullptr;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  NS_ENSURE_TRUE(mIsCursorable, NS_ERROR_NOT_IMPLEMENTED);
+  if (!(mDocFlags & eCursorable))
+    return NS_OK;
 
   if (!mVirtualCursor) {
     mVirtualCursor = new nsAccessiblePivot(this);
     mVirtualCursor->AddObserver(this);
   }
 
   NS_ADDREF(*aVirtualCursor = mVirtualCursor);
   return NS_OK;
@@ -598,18 +587,16 @@ DocAccessible::Shutdown()
   if (!mPresShell) // already shutdown
     return;
 
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eDocDestroy))
     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
@@ -635,16 +622,17 @@ DocAccessible::Shutdown()
 
   mChildDocuments.Clear();
 
   if (mVirtualCursor) {
     mVirtualCursor->RemoveObserver(this);
     mVirtualCursor = nullptr;
   }
 
+  mPresShell->SetDocAccessible(nullptr);
   mPresShell = nullptr;  // Avoid reentrancy
 
   mDependentIDsHash.Clear();
   mNodeToAccessibleMap.Clear();
   ClearCache(mAccessibleCache);
 
   HyperTextAccessibleWrap::Shutdown();
 
@@ -793,46 +781,16 @@ DocAccessible::ScrollTimerCallback(nsITi
     if (docAcc->mScrollWatchTimer) {
       docAcc->mScrollWatchTimer->Cancel();
       docAcc->mScrollWatchTimer = nullptr;
       NS_RELEASE(docAcc); // Release kung fu death grip
     }
   }
 }
 
-// DocAccessible protected member
-void
-DocAccessible::AddScrollListener()
-{
-  if (!mPresShell)
-    return;
-
-  nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollableExternal();
-  if (sf) {
-    sf->AddScrollPositionListener(this);
-#ifdef A11Y_LOG
-    if (logging::IsEnabled(logging::eDocCreate))
-      logging::Text("add scroll listener");
-#endif
-  }
-}
-
-// DocAccessible protected member
-void
-DocAccessible::RemoveScrollListener()
-{
-  if (!mPresShell)
-    return;
- 
-  nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollableExternal();
-  if (sf) {
-    sf->RemoveScrollPositionListener(this);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // nsIScrollPositionListener
 
 void
 DocAccessible::ScrollPositionDidChange(nscoord aX, nscoord aY)
 {
   // Start new timer, if the timer cycles at least 1 full cycle without more scroll position changes,
   // then the ::Notify() method will fire the accessibility event for scroll position changes
@@ -1506,24 +1464,33 @@ DocAccessible::NotifyOfLoading(bool aIsR
   nsRefPtr<AccEvent> stateEvent =
     new AccStateChangeEvent(this, states::BUSY, true);
   FireDelayedEvent(stateEvent);
 }
 
 void
 DocAccessible::DoInitialUpdate()
 {
+  if (nsCoreUtils::IsTabDocument(mDocumentNode))
+    mDocFlags |= eTabDocument;
+
+  // We provide a virtual cursor if this is a root doc or if it's a tab doc.
+  if (!mDocumentNode->GetParentDocument() || (mDocFlags & eTabDocument))
+    mDocFlags |= eCursorable;
+
   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(mDocumentNode);
-  if (mContent != contentElm)
+  if (mContent != contentElm) {
     mContent = contentElm;
+    SetRoleMapEntry(aria::GetRoleMap(mContent));
+  }
 
   // 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
   // this document may be fired prior to this reorder event. If this is
   // a problem then consider to keep event processing per tab document.
@@ -1734,18 +1701,20 @@ DocAccessible::ProcessContentInserted(Ac
       continue;
 
     if (containerNotUpdated) {
       containerNotUpdated = false;
 
       if (aContainer == this) {
         // If new root content has been inserted then update it.
         nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocumentNode);
-        if (rootContent != mContent)
+        if (rootContent != mContent) {
           mContent = rootContent;
+          SetRoleMapEntry(aria::GetRoleMap(mContent));
+        }
 
         // 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.
       }
 
       // XXX: Invalidate parent-child relations for container accessible and its
       // children because there's no good way to find insertion point of new child
--- a/accessible/src/generic/DocAccessible.h
+++ b/accessible/src/generic/DocAccessible.h
@@ -328,18 +328,21 @@ protected:
   virtual void DoInitialUpdate();
 
   /**
    * Process document load notification, fire document load and state busy
    * events if applicable.
    */
   void ProcessLoad();
 
-    void AddScrollListener();
-    void RemoveScrollListener();
+  /**
+   * Add/remove scroll listeners, @see nsIScrollPositionListener interface.
+   */
+  void AddScrollListener();
+  void RemoveScrollListener();
 
   /**
    * Append the given document accessible to this document's child document
    * accessibles.
    */
   bool AppendChildDocument(DocAccessible* aChildDocument)
   {
     return mChildDocuments.AppendElement(aChildDocument);
@@ -478,30 +481,49 @@ protected:
    * @param aTimer    [in] the timer object
    * @param aClosure  [in] the document accessible where scrolling happens
    */
   static void ScrollTimerCallback(nsITimer* aTimer, void* aClosure);
 
 protected:
 
   /**
+   * State and property flags, kept by mDocFlags.
+   */
+  enum {
+    // Whether scroll listeners were added.
+    eScrollInitialized = 1 << 0,
+
+    // Whether we support nsIAccessibleCursorable.
+    eCursorable = 1 << 1,
+
+    // Whether the document is a tab document.
+    eTabDocument = 1 << 2
+  };
+
+  /**
    * Cache of accessibles within this document accessible.
    */
   AccessibleHashtable mAccessibleCache;
   nsDataHashtable<nsPtrHashKey<const nsINode>, Accessible*>
     mNodeToAccessibleMap;
 
     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;
+  uint32_t mLoadState : 3;
+
+  /**
+   * Bit mask of other states and props.
+   */
+  uint32_t mDocFlags : 28;
 
   /**
    * Type of document load event fired after the document is loaded completely.
    */
   uint32_t mLoadEventType;
 
   /**
    * Reference to anchor jump element.
@@ -512,21 +534,16 @@ protected:
    * Keep the ARIA attribute old value that is initialized by
    * AttributeWillChange and used by AttributeChanged notifications.
    */
   nsIAtom* mARIAAttrOldValue;
 
   nsTArray<nsRefPtr<DocAccessible> > mChildDocuments;
 
   /**
-   * Whether we support nsIAccessibleCursorable, used when querying the interface.
-   */
-  bool mIsCursorable;
-
-  /**
    * The virtual cursor of the document when it supports nsIAccessibleCursorable.
    */
   nsRefPtr<nsAccessiblePivot> mVirtualCursor;
 
   /**
    * A storage class for pairing content with one of its relation attributes.
    */
   class AttrRelProvider
--- a/accessible/src/html/HTMLFormControlAccessible.cpp
+++ b/accessible/src/html/HTMLFormControlAccessible.cpp
@@ -353,17 +353,17 @@ HTMLTextFieldAccessible::Value(nsString&
   if (NativeState() & states::PROTECTED)    // Don't return password text!
     return;
 
   nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea(do_QueryInterface(mContent));
   if (textArea) {
     textArea->GetValue(aValue);
     return;
   }
-  
+
   nsHTMLInputElement* input = nsHTMLInputElement::FromContent(mContent);
   if (input)
     input->GetValue(aValue);
 }
 
 void
 HTMLTextFieldAccessible::ApplyARIAState(uint64_t* aState) const
 {
--- a/accessible/src/html/HTMLTableAccessible.cpp
+++ b/accessible/src/html/HTMLTableAccessible.cpp
@@ -497,17 +497,18 @@ HTMLTableAccessible::SelectedCellCount()
     for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
       nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
       if (!cellFrame || !cellFrame->IsSelected())
         continue;
 
       int32_t startRow = -1, startCol = -1;
       cellFrame->GetRowIndex(startRow);
       cellFrame->GetColIndex(startCol);
-      if (startRow == rowIdx && startCol == colIdx)
+      if (startRow >= 0 && (uint32_t)startRow == rowIdx &&
+          startCol >= 0 && (uint32_t)startCol == colIdx)
         count++;
     }
   }
 
   return count;
 }
 
 uint32_t
@@ -546,17 +547,18 @@ HTMLTableAccessible::SelectedCells(nsTAr
     for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
       nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
       if (!cellFrame || !cellFrame->IsSelected())
         continue;
 
       int32_t startCol = -1, startRow = -1;
       cellFrame->GetRowIndex(startRow);
       cellFrame->GetColIndex(startCol);
-      if (startRow != rowIdx || startCol != colIdx)
+      if ((startRow >= 0 && (uint32_t)startRow != rowIdx) ||
+          (startCol >= 0 && (uint32_t)startCol != colIdx))
         continue;
 
       Accessible* cell = mDoc->GetAccessible(cellFrame->GetContent());
         aCells->AppendElement(cell);
     }
   }
 }
 
@@ -572,17 +574,18 @@ HTMLTableAccessible::SelectedCellIndices
     for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
       nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
       if (!cellFrame || !cellFrame->IsSelected())
         continue;
 
       int32_t startRow = -1, startCol = -1;
       cellFrame->GetColIndex(startCol);
       cellFrame->GetRowIndex(startRow);
-      if (startRow == rowIdx && startCol == colIdx)
+      if (startRow >= 0 && (uint32_t)startRow == rowIdx &&
+          startCol >= 0 && (uint32_t)startCol == colIdx)
         aCells->AppendElement(CellIndexAt(rowIdx, colIdx));
     }
   }
 }
 
 void
 HTMLTableAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols)
 {
--- a/accessible/src/jsat/EventManager.jsm
+++ b/accessible/src/jsat/EventManager.jsm
@@ -186,17 +186,17 @@ this.EventManager = {
             text = txtIface.
               getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT);
           } catch (x) {
             // XXX we might have gotten an exception with of a
             // zero-length text. If we did, ignore it (bug #749810).
             if (txtIface.characterCount)
               throw x;
           }
-          this.present(Presentation, textChanged(
+          this.present(Presentation.textChanged(
                          isInserted, event.start, event.length,
                          text, event.modifiedText));
         }
         break;
       }
       case Ci.nsIAccessibleEvent.EVENT_FOCUS:
       {
         // Put vc where the focus is at
--- a/accessible/src/mac/AccessibleWrap.mm
+++ b/accessible/src/mac/AccessibleWrap.mm
@@ -55,19 +55,19 @@ AccessibleWrap::GetNativeInterface (void
 
 // overridden in subclasses to create the right kind of object. by default we create a generic
 // 'mozAccessible' node.
 Class
 AccessibleWrap::GetNativeType () 
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
-  if (IsXULDeck())
+  if (IsXULTabpanels())
     return [mozPaneAccessible class];
-  
+
   roles::Role role = Role();
   switch (role) {
     case roles::PUSHBUTTON:
     case roles::SPLITBUTTON:
     case roles::TOGGLE_BUTTON:
     {
       // if this button may show a popup, let's make it of the popupbutton type.
       return HasPopup() ? [mozPopupButtonAccessible class] : 
--- a/accessible/src/msaa/DocAccessibleWrap.cpp
+++ b/accessible/src/msaa/DocAccessibleWrap.cpp
@@ -218,17 +218,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(mDocumentNode)) {
+    if (mDocFlags & eTabDocument) {
       sHWNDCache.Remove(mHWND);
       ::DestroyWindow(static_cast<HWND>(mHWND));
     }
 
     mHWND = nullptr;
   }
 
   DocAccessible::Shutdown();
@@ -248,17 +248,17 @@ DocAccessibleWrap::GetNativeWindow() con
 
 void
 DocAccessibleWrap::DoInitialUpdate()
 {
   DocAccessible::DoInitialUpdate();
 
   if (nsWinUtils::IsWindowEmulationStarted()) {
     // Create window for tab document.
-    if (nsCoreUtils::IsTabDocument(mDocumentNode)) {
+    if (mDocFlags & eTabDocument) {
       mozilla::dom::TabChild* tabChild =
         mozilla::dom::GetTabChildFrom(mDocumentNode->GetShell());
 
       a11y::RootAccessible* rootDocument = RootAccessible();
 
       mozilla::WindowsHandle nativeData = 0;
       if (tabChild)
         tabChild->SendGetWidgetNativeData(&nativeData);
--- a/accessible/src/windows/sdn/sdnAccessible-inl.h
+++ b/accessible/src/windows/sdn/sdnAccessible-inl.h
@@ -13,19 +13,17 @@
 #include "nsAccessibilityService.h"
 
 namespace mozilla {
 namespace a11y {
 
 inline DocAccessible*
 sdnAccessible::GetDocument() const
 {
-  DocManager* docMgr = GetAccService();
-  return docMgr ?
-    docMgr->GetDocAccessibleFromCache(mNode->OwnerDoc()) : nullptr;
+  return GetExistingDocAccessible(mNode->OwnerDoc());
 }
 
 inline Accessible*
 sdnAccessible::GetAccessible() const
 {
   DocAccessible* document = GetDocument();
   return document ? document->GetAccessible(mNode) : nullptr;
 }
--- a/accessible/src/xul/XULTabAccessible.cpp
+++ b/accessible/src/xul/XULTabAccessible.cpp
@@ -158,21 +158,21 @@ ENameValueFlag
 XULTabsAccessible::NativeName(nsString& aName)
 {
   // no name
   return eNameOK;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
-// XULDeckAccessible
+// XULTabpanelsAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 role
-XULDeckAccessible::NativeRole()
+XULTabpanelsAccessible::NativeRole()
 {
   return roles::PANE;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULTabpanelAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
--- a/accessible/src/xul/XULTabAccessible.h
+++ b/accessible/src/xul/XULTabAccessible.h
@@ -57,22 +57,22 @@ protected:
   // Accessible
   virtual ENameValueFlag NativeName(nsString& aName) MOZ_OVERRIDE;
 };
 
 
 /**
  * A container of tab panels, xul:tabpanels element.
  */
-class XULDeckAccessible : public AccessibleWrap
+class XULTabpanelsAccessible : public AccessibleWrap
 {
 public:
-  XULDeckAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+  XULTabpanelsAccessible(nsIContent* aContent, DocAccessible* aDoc) :
     AccessibleWrap(aContent, aDoc)
-    { mType = eXULDeckType; }
+    { mType = eXULTabpanelsType; }
 
   // Accessible
   virtual a11y::role NativeRole();
 };
 
 
 /**
  * A tabpanel object, child elements of xul:tabpanels element. Note,the object
--- a/accessible/tests/mochitest/name.js
+++ b/accessible/tests/mochitest/name.js
@@ -12,8 +12,21 @@ function testName(aAccOrElmOrID, aName, 
   var txtID = prettyName(aAccOrElmOrID);
   try {
     is(acc.name, aName, msg + "Wrong name of the accessible for " + txtID);
   } catch (e) {
     ok(false, msg + "Can't get name of the accessible for " + txtID);
   }
   return acc;
 }
+
+/**
+ * Test accessible description for the given accessible.
+ */
+function testDescr(aAccOrElmOrID, aDescr)
+{
+  var acc = getAccessible(aAccOrElmOrID);
+  if (!acc)
+   return;
+
+  is(acc.description, aDescr,
+     "Wrong description for " + prettyName(aAccOrElmOrID));
+}
--- a/accessible/tests/mochitest/name/Makefile.in
+++ b/accessible/tests/mochitest/name/Makefile.in
@@ -16,14 +16,15 @@ MOCHITEST_A11Y_FILES =\
 		general.xbl \
 		markup.js \
 		test_button.html \
 		test_general.html \
 		test_general.xul \
 		test_link.html \
 		test_list.html \
 		test_markup.html \
+		test_svg.html \
 		test_browserui.xul \
 		test_tree.xul \
 		markuprules.xml \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/name/test_svg.html
@@ -0,0 +1,56 @@
+<html>
+
+<head>
+  <title>Accessible name and description for SVG elements</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../name.js"></script>
+
+  <script type="application/javascript">
+
+    function doTest()
+    {
+      testName("svg1", "A name");
+      testDescr("svg1", "A description");
+      testName("svg2", "A tooltip");
+      testDescr("svg2", "");
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=459357"
+     title="Support accessible name computation for SVG">
+    Mozilla Bug 459357
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg1">
+    <title>A description</title>
+    <desc>A name</desc>
+  </svg>
+
+  <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg2">
+    <title>A tooltip</title>
+  </svg>
+
+</body>
+</html>
--- a/accessible/tests/mochitest/states/test_frames.html
+++ b/accessible/tests/mochitest/states/test_frames.html
@@ -27,17 +27,16 @@
       testStates(frameDoc, STATE_READONLY, 0, 0, 0,
                  "test1: frameDoc");
       testStates(frameDocArticle, STATE_READONLY, 0, 0, 0,
                  "test1: frameDocArticle");
       testStates(frameDocCheckbox, 0, 0, STATE_READONLY, 0,
                  "test1: frameDocCheckbox");
       testStates(frameDocTextbox, 0, EXT_STATE_EDITABLE, STATE_READONLY, 0,
                  "test1: frameDocTextbox");
-
       frameDoc.designMode = "on";
       testStates(frameDoc,  0, EXT_STATE_EDITABLE, STATE_READONLY, 0,
                  "test2: frameDoc");
       testStates(frameDocArticle, STATE_READONLY, 0, 0, 0,
                  "test2: frameDocArticle");
       testStates(frameDocCheckbox, 0, 0, STATE_READONLY, 0,
                  "test2: frameDocCheckbox");
       testStates(frameDocTextbox, 0, EXT_STATE_EDITABLE, STATE_READONLY, 0,
--- a/accessible/tests/mochitest/states/test_visibility.xul
+++ b/accessible/tests/mochitest/states/test_visibility.xul
@@ -15,17 +15,16 @@
           src="../role.js" />
   <script type="application/javascript"
           src="../states.js" />
 
   <script type="application/javascript">
   <![CDATA[
     function doTest()
     {
-      testStates("deck_pane1", STATE_INVISIBLE, 0, STATE_OFFSCREEN);
       testStates("deck_pane2", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
       testStates("tabs_pane1", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
       testStates("tabs_pane2", STATE_OFFSCREEN, 0, STATE_INVISIBLE);
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
--- a/accessible/tests/mochitest/test_descr.html
+++ b/accessible/tests/mochitest/test_descr.html
@@ -3,28 +3,20 @@
 <head>
   <title>nsIAccessible::description tests</title>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="common.js"></script>
+  <script type="application/javascript"
+          src="name.js"></script>
 
   <script type="application/javascript">
-    function testDescr(aAccOrElmOrID, aDescr)
-    {
-      var acc = getAccessible(aAccOrElmOrID);
-      if (!acc)
-        return;
-
-      is(acc.description, aDescr,
-         "Wrong description for " + prettyName(aAccOrElmOrID));
-    }
-
     function doTest()
     {
       // Description from aria-describedby attribute
       testDescr("img1", "aria description");
 
       // No description from @title attribute because it is used to generate
       // name.
       testDescr("img2", "");
--- a/accessible/tests/mochitest/treeupdate/Makefile.in
+++ b/accessible/tests/mochitest/treeupdate/Makefile.in
@@ -12,16 +12,17 @@ relativesrcdir  = accessible/treeupdate
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_A11Y_FILES =\
 		test_ariadialog.html \
 		test_canvas.html \
 		test_colorpicker.xul \
 		test_cssoverflow.html \
 		test_contextmenu.xul \
+		test_deck.xul \
 		test_doc.html \
 		test_gencontent.html \
 		test_hidden.html \
 		test_imagemap.html \
 		test_list_editabledoc.html \
 		test_list.html \
 		test_listbox.xul \
 		test_menu.xul \
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_deck.xul
@@ -0,0 +1,109 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Tree update on XUL deck panel switching">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+  <script type="application/javascript"
+          src="../common.js" />
+  <script type="application/javascript"
+          src="../role.js" />
+  <script type="application/javascript"
+          src="../states.js" />
+  <script type="application/javascript"
+          src="../events.js" />
+
+  <script type="application/javascript">
+  <![CDATA[
+    function switchDeckPanel(aContainerID, aDeckID)
+    {
+      this.panelIndex = 0;
+
+      this.container = getAccessible(aContainerID);
+      this.deckNode = getNode(aDeckID);
+      this.prevPanel = getAccessible(this.deckNode.selectedPanel);
+      this.panelNode = this.deckNode.childNodes[this.panelIndex];
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, this.prevPanel),
+        new invokerChecker(EVENT_SHOW, this.panelNode),
+        new invokerChecker(EVENT_REORDER, this.container)
+      ];
+
+      this.invoke = function switchDeckPanel_invoke()
+      {
+        var tree =
+          { GROUPING: [ // role="group"
+            { GROUPING: [ // groupbox, a selected panel #2
+              { PUSHBUTTON: [ ] } // button
+            ] }
+          ] };
+        testAccessibleTree(this.container, tree);
+
+        this.deckNode.selectedIndex = this.panelIndex;
+      }
+
+      this.finalCheck = function switchDeckPanel_finalCheck()
+      {
+        var tree =
+          { GROUPING: [ // role="group"
+            { LABEL: [ // description, a selected panel #1
+              { TEXT_LEAF: []  } // text leaf, a description value
+            ] }
+          ] };
+        testAccessibleTree(this.container, tree);
+      }
+
+      this.getID = function switchDeckPanel_getID()
+      {
+        return "switch deck panel";
+      }
+    }
+
+    var gQueue = null;
+    function doTest()
+    {
+      gQueue = new eventQueue();
+      gQueue.push(new switchDeckPanel("container", "deck"));
+      gQueue.invoke(); // will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  ]]>
+  </script>
+
+  <hbox flex="1" style="overflow: auto;">
+    <body xmlns="http://www.w3.org/1999/xhtml">
+     <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=814836"
+         title=" xul:deck element messes up screen reader">
+        Mozilla Bug 814836
+     </a>
+
+      <p id="display"></p>
+      <div id="content" style="display: none">
+      </div>
+      <pre id="test">
+      </pre>
+    </body>
+
+    <vbox flex="1" id="container" role="group">
+
+      <deck id="deck" selectedIndex="1">
+        <description>This is the first page</description>
+        <groupbox>
+          <button label="This is the second page"/>
+        </groupbox>
+      </deck>
+
+    </vbox>
+  </hbox>
+
+</window>
+
--- a/accessible/tests/mochitest/treeupdate/test_doc.html
+++ b/accessible/tests/mochitest/treeupdate/test_doc.html
@@ -27,27 +27,27 @@
     {
       return getNode(aID).contentDocument;
     }
     function getDocChildNode(aID)
     {
       return getDocNode(aID).body.firstChild;
     }
 
-    function rootContentReplaced(aID, aTextName)
+    function rootContentReplaced(aID, aTextName, aRootContentRole)
     {
       this.eventSeq = [
         new invokerChecker(EVENT_SHOW, getDocChildNode, aID),
         new invokerChecker(EVENT_REORDER, getDocNode, aID)
       ];
 
       this.finalCheck = function rootContentReplaced_finalCheck()
       {
         var tree = {
-          role: ROLE_DOCUMENT,
+          role: aRootContentRole || ROLE_DOCUMENT,
           children: [
             {
               role: ROLE_TEXT_LEAF,
               name: aTextName
             }
           ]
         };
         testAccessibleTree(getDocNode(aID), tree);
@@ -137,59 +137,69 @@
         var newHTMLNode = docNode.createElement("html");
         var newBodyNode = docNode.createElement("body");
         var newTextNode = docNode.createTextNode("New Wave");
         newBodyNode.appendChild(newTextNode);
         newHTMLNode.appendChild(newBodyNode);
         docNode.replaceChild(newHTMLNode, docNode.documentElement);
       }
 
-      this.getID = function replaceIFrameBody_getID()
+      this.getID = function replaceIFrameHTMLElm_getID()
       {
         return "replace HTML element";
       }
     }
 
     /**
-     * Replace HTML body.
+     * Replace HTML body on new body having ARIA role.
      */
     function replaceIFrameBody(aID)
     {
       this.__proto__ = new rootContentReplaced(aID, "New Hello");
 
       this.invoke = function replaceIFrameBody_invoke()
       {
         var docNode = getDocNode(aID);
         var newBodyNode = docNode.createElement("body");
         var newTextNode = docNode.createTextNode("New Hello");
         newBodyNode.appendChild(newTextNode);
         docNode.documentElement.replaceChild(newBodyNode, docNode.body);
       }
 
-      this.finalCheck = function replaceIFrameBody_finalCheck()
-      {
-        var tree = {
-          role: ROLE_DOCUMENT,
-          children: [
-            {
-              role: ROLE_TEXT_LEAF,
-              name: "New Hello"
-            }
-          ]
-        };
-        testAccessibleTree(getDocNode(aID), tree);
-      }
-
       this.getID = function replaceIFrameBody_getID()
       {
         return "replace body";
       }
     }
 
     /**
+     * Replace HTML body on new body having ARIA role.
+     */
+    function replaceIFrameBodyOnARIARoleBody(aID)
+    {
+      this.__proto__ = new rootContentReplaced(aID, "New Hello",
+                                               ROLE_PUSHBUTTON);
+
+      this.invoke = function replaceIFrameBodyOnARIARoleBody_invoke()
+      {
+        var docNode = getDocNode(aID);
+        var newBodyNode = docNode.createElement("body");
+        var newTextNode = docNode.createTextNode("New Hello");
+        newBodyNode.appendChild(newTextNode);
+        newBodyNode.setAttribute("role", "button");
+        docNode.documentElement.replaceChild(newBodyNode, docNode.body);
+      }
+
+      this.getID = function replaceIFrameBodyOnARIARoleBody_getID()
+      {
+        return "replace body on body having ARIA role";
+      }
+    }
+
+    /**
      * Open/close document pair.
      */
     function openIFrameDoc(aID)
     {
       this.__proto__ = new rootContentRemoved(aID);
 
       this.invoke = function openIFrameDoc_invoke()
       {
@@ -396,16 +406,17 @@
       gQueue.push(new openIFrameDoc("iframe"));
       gQueue.push(new closeIFrameDoc("iframe"));
       gQueue.push(new removeHTMLFromIFrameDoc("iframe"));
       gQueue.push(new insertHTMLToIFrameDoc("iframe"));
       gQueue.push(new removeBodyFromIFrameDoc("iframe"));
       gQueue.push(new insertElmUnderDocElmWhileBodyMissed("iframe"));
       gQueue.push(new insertBodyToIFrameDoc("iframe"));
       gQueue.push(new changeSrc("iframe"));
+      gQueue.push(new replaceIFrameBodyOnARIARoleBody("iframe"));
 
       gQueue.invoke(); // SimpleTest.finish() will be called in the end
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
@@ -415,16 +426,19 @@
      title="Update accessible tree when root element is changed"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=606082">Mozilla Bug 606082</a>
   <a target="_blank"
      title="Elements inserted outside the body aren't accessible"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=608887">Mozilla Bug 608887</a>
   <a target="_blank"
      title="Reorder event for document must be fired after document initial tree creation"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=669263">Mozilla Bug 669263</a>
+  <a target="_blank"
+     title="Changing the HTML body doesn't pick up ARIA role"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=818407">Mozilla Bug 818407</a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <iframe id="iframe"></iframe>
 
--- a/accessible/tests/mochitest/value/test_general.html
+++ b/accessible/tests/mochitest/value/test_general.html
@@ -45,16 +45,31 @@
       testValue("aria_checkbox_link", "");
       testValue("aria_application_link", "");
 
       // roles that can live as HTMLLinkAccessibles
       testValue("aria_link_link", href);
       testValue("aria_main_link", href);
       testValue("aria_navigation_link", href);
 
+      //////////////////////////////////////////////////////////////////////////
+      // ARIA comboboxes
+
+      // aria-activedescendant defines a current item the value is computed from
+      testValue("aria_combobox1", kDiscBulletText + "Zoom");
+
+      // aria-selected defines a selected item the value is computed from,
+      // list control is pointed by aria-owns relation.
+      testValue("aria_combobox2", kDiscBulletText + "Zoom");
+
+      // aria-selected defines a selected item the value is computed from,
+      // list control is a child of combobox.
+      testValue("aria_combobox3", kDiscBulletText + "2");
+
+      //////////////////////////////////////////////////////////////////////////
       // HTML controls
       testValue("combobox1", "item1");
       testValue("combobox2", "item2");
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
@@ -64,17 +79,22 @@
 </head>
 
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=494807"
      title="Do not expose a11y info specific to hyperlinks when role is overridden using ARIA">
     Mozilla Bug 494807
-  </a><br />
+  </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=819273"
+     title=" ARIA combobox should have accessible value">
+    Mozilla Bug 819273
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none">
   </div>
   <pre id="test">
   </pre>
 
   <a id="aria_menuitem_link" role="menuitem" href="foo">menuitem</a>
   <a id="aria_button_link" role="button" href="foo">button</a>
@@ -83,16 +103,42 @@
   <!-- landmark links -->
   <a id="aria_application_link" role="application" href="foo">app</a>
   <a id="aria_main_link" role="main" href="foo">main</a>
   <a id="aria_navigation_link" role="navigation" href="foo">nav</a>
 
   <!-- strange edge case: please don't do this in the wild -->
   <a id="aria_link_link" role="link" href="foo">link</a>
 
+  <div id="aria_combobox1" role="combobox"
+       aria-owns="aria_combobox1_owned_listbox"
+       aria-activedescendant="aria_combobox1_selected_option">
+  </div>
+  <ul role="listbox" id="aria_combobox1_owned_listbox">
+    <li role="option">Zebra</li>
+    <li role="option" id="aria_combobox1_selected_option">Zoom</li>
+  </ul>
+
+  <div id="aria_combobox2" role="combobox"
+       aria-owns="aria_combobox2_owned_listbox">
+  </div>
+  <ul role="listbox" id="aria_combobox2_owned_listbox">
+    <li role="option">Zebra</li>
+    <li role="option" aria-selected="true">Zoom</li>
+  </ul>
+
+  <div id="aria_combobox3" role="combobox">
+    <div role="textbox"></div>
+    <ul role="listbox">
+      <li role="option">1</li>
+      <li role="option" aria-selected="true">2</li>
+      <li role="option">3</li>
+    </ul>
+  </div>
+
   <select id="combobox1">
     <option id="cb1_item1">item1</option>
     <option id="cb1_item2">item2</option>
   </select>
   <select id="combobox2">
     <option id="cb2_item1">item1</option>
     <option id="cb2_item2" selected="true">item2</option>
   </select>
--- a/b2g/app/BootAnimation.cpp
+++ b/b2g/app/BootAnimation.cpp
@@ -358,19 +358,18 @@ AnimationThread(void *)
 
     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     eglInitialize(display, nullptr, nullptr);
 
     int format;
     ANativeWindow const * const window = gNativeWindow.get();
     window->query(window, NATIVE_WINDOW_FORMAT, &format);
 
-    EGLConfig config = NULL;
-    CreateConfig(&config, display, format);
-    if (!config) {
+    EGLConfig config;
+    if (!CreateConfig(&config, display, format)) {
         LOGW("Could not find config for pixel format");
         return nullptr;
     }
 
     EGLSurface surface = eglCreateWindowSurface(display, config, gNativeWindow.get(), nullptr);
 
     const cdir_entry *entry = nullptr;
     const local_file_header *file = nullptr;
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -328,17 +328,17 @@ pref("urlclassifier.gethashnoise", 4);
 pref("urlclassifier.randomizeclient", false);
 
 // The list of tables that use the gethash request to confirm partial results.
 pref("urlclassifier.gethashtables", "goog-phish-shavar,goog-malware-shavar");
 
 // If an urlclassifier table has not been updated in this number of seconds,
 // a gethash request will be forced to check that the result is still in
 // the database.
-pref("urlclassifier.confirm-age", 2700);
+pref("urlclassifier.max-complete-age", 2700);
 
 // URL for checking the reason for a malware warning.
 pref("browser.safebrowsing.malware.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
 #endif
 
 // True if this is the first time we are showing about:firstrun
 pref("browser.firstrun.show.uidiscovery", true);
 pref("browser.firstrun.show.localepicker", true);
@@ -351,23 +351,16 @@ pref("content.ime.strict_policy", true);
 // On Android, you also need to do the following for the output
 // to show up in logcat:
 //
 // $ adb shell stop
 // $ adb shell setprop log.redirect-stdio true
 // $ adb shell start
 pref("browser.dom.window.dump.enabled", false);
 
-
-
-// Temporarily relax file:// origin checks so that we can use <img>s
-// from other dirs as webgl textures and more.  Remove me when we have
-// installable apps or wifi support.
-pref("security.fileuri.strict_origin_policy", false);
-
 // Default Content Security Policy to apply to privileged and certified apps
 pref("security.apps.privileged.CSP.default", "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'");
 pref("security.apps.certified.CSP.default", "default-src *; script-src 'self'; object-src 'none'; style-src 'self'");
 
 // Temporarily force-enable GL compositing.  This is default-disabled
 // deep within the bowels of the widgetry system.  Remove me when GL
 // compositing isn't default disabled in widget/android.
 pref("layers.acceleration.force-enabled", true);
@@ -456,19 +449,16 @@ pref("marionette.defaultPrefs.port", 282
 #ifdef MOZ_UPDATER
 // When we're applying updates, we can't let anything hang us on
 // quit+restart.  The user has no recourse.
 pref("shutdown.watchdog.timeoutSecs", 5);
 // Timeout before the update prompt automatically installs the update
 pref("b2g.update.apply-prompt-timeout", 60000); // milliseconds
 // Amount of time to wait after the user is idle before prompting to apply an update
 pref("b2g.update.apply-idle-timeout", 600000); // milliseconds
-// Amount of time the updater waits for the process to exit cleanly before
-// forcefully exiting the process
-pref("b2g.update.self-destruct-timeout", 5000); // milliseconds
 
 pref("app.update.enabled", true);
 pref("app.update.auto", false);
 pref("app.update.silent", false);
 pref("app.update.mode", 0);
 pref("app.update.incompatible.mode", 0);
 pref("app.update.staging.enabled", true);
 pref("app.update.service.enabled", true);
@@ -596,16 +586,23 @@ pref("ui.mouse.radius.bottommm", 2);
 pref("browser.prompt.allowNative", false);
 
 // Minimum delay in milliseconds between network activity notifications (0 means
 // no notifications). The delay is the same for both download and upload, though
 // they are handled separately. This pref is only read once at startup:
 // a restart is required to enable a new value.
 pref("network.activity.blipIntervalMilliseconds", 250);
 
+// By default we want the NetworkManager service to manage Gecko's offline
+// status for us according to the state of Wifi/cellular data connections.
+// In some environments, such as the emulator or hardware with other network
+// connectivity, this is not desireable, however, in which case this pref
+// can be flipped to false.
+pref("network.gonk.manage-offline-status", true);
+
 pref("jsloader.reuseGlobal", true);
 
 // Enable font inflation for browser tab content.
 pref("font.size.inflation.minTwips", 120);
 // And disable it for lingering master-process UI.
 pref("font.size.inflation.disabledInMasterProcess", true);
 
 // Enable freeing dirty pages when minimizing memory; this reduces memory
--- a/b2g/chrome/content/forms.js
+++ b/b2g/chrome/content/forms.js
@@ -277,24 +277,22 @@ function getJSON(element) {
   // Treat contenteditble element as a special text field
   if (element.contentEditable && element.contentEditable == "true") {
     type = "text";
     value = element.textContent;
   }
 
   // Until the input type=date/datetime/time have been implemented
   // let's return their real type even if the platform returns 'text'
-  // Related to Bug 769352 - Implement <input type=date>
   // Related to Bug 777279 - Implement <input type=time>
   let attributeType = element.getAttribute("type") || "";
 
   if (attributeType) {
     var typeLowerCase = attributeType.toLowerCase(); 
     switch (typeLowerCase) {
-      case "date":
       case "time":
       case "datetime":
       case "datetime-local":
         type = typeLowerCase;
         break;
     }
   }
 
--- a/b2g/chrome/content/identity.js
+++ b/b2g/chrome/content/identity.js
@@ -93,29 +93,30 @@ function doInternalWatch() {
       }
     );
   }
 }
 
 function doInternalRequest() {
   log("doInternalRequest:", options && isLoaded);
   if (options && isLoaded) {
+    var stringifiedOptions = JSON.stringify(options);
     content.wrappedJSObject.BrowserID.internal.get(
       options.origin,
       function(assertion, internalParams) {
         internalParams = internalParams || {};
         if (assertion) {
           identityCall({
             method: 'login',
             assertion: assertion,
             _internalParams: internalParams});
         }
         closeIdentityDialog();
       },
-      options);
+      stringifiedOptions);
   }
 }
 
 function doInternalLogout(aOptions) {
   log("doInternalLogout:", (options && isLoaded));
   if (options && isLoaded) {
     let BrowserID = content.wrappedJSObject.BrowserID;
     BrowserID.internal.logout(options.origin, function() {
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -98,17 +98,17 @@ var shell = {
       // For chrome crashes, we want to report the lastRunCrashID.
       if (isChrome) {
         crashID = Cc["@mozilla.org/xre/app-info;1"]
                     .getService(Ci.nsIXULRuntime).lastRunCrashID;
       }
     } catch(e) { }
 
     // Bail if there isn't a valid crashID.
-    if (!crashID) {
+    if (!crashID && !this.CrashSubmit.pendingIDs().length) {
       return;
     }
 
     // purge the queue.
     this.CrashSubmit.pruneSavedDumps();
 
     try {
       // Check if we should automatically submit this crash.
@@ -120,34 +120,38 @@ var shell = {
     // Let Gaia notify the user of the crash.
     this.sendChromeEvent({
       type: "handle-crash",
       crashID: crashID,
       chrome: isChrome
     });
   },
 
+  // this function submit the pending crashes.
+  // make sure you are online.
+  submitQueuedCrashes: function shell_submitQueuedCrashes() {
+    // submit the pending queue.
+    let pending = shell.CrashSubmit.pendingIDs();
+    for (let crashid of pending) {
+      shell.CrashSubmit.submit(crashid);
+    }
+  },
+
   // This function submits a crash when we're online.
   submitCrash: function shell_submitCrash(aCrashID) {
     if (this.onlineForCrashReport()) {
-      this.CrashSubmit.submit(aCrashID);
+      this.submitQueuedCrashes();
       return;
     }
 
     Services.obs.addObserver(function observer(subject, topic, state) {
       let network = subject.QueryInterface(Ci.nsINetworkInterface);
       if (network.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED
           && network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
-        shell.CrashSubmit.submit(aCrashID);
-
-        // submit the pending queue.
-        let pending = shell.CrashSubmit.pendingIDs();
-        for (let crashid of pending) {
-          shell.CrashSubmit.submit(crashid);
-        }
+        shell.submitQueuedCrashes();
 
         Services.obs.removeObserver(observer, topic);
       }
     }, "network-interface-state-changed", false);
   },
 
   get contentBrowser() {
     delete this.contentBrowser;
@@ -679,17 +683,17 @@ var AlertsHelper = {
       let getNotificationURLFor = function(messages) {
         if (!messages)
           return null;
 
         for (let i = 0; i < messages.length; i++) {
           let message = messages[i];
           if (message === "notification") {
             return helper.fullLaunchPath();
-          } else if ("notification" in message) {
+          } else if (typeof message == "object" && "notification" in message) {
             return helper.resolveFromOrigin(message["notification"]);
           }
         }
       }
 
       listener.target = getNotificationURLFor(manifest.messages);
 
       // Bug 816944 - Support notification messages for entry_points.
--- a/b2g/components/UpdatePrompt.js
+++ b/b2g/components/UpdatePrompt.js
@@ -13,24 +13,19 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/Services.jsm");
 
 const VERBOSE = 1;
 let log =
   VERBOSE ?
   function log_dump(msg) { dump("UpdatePrompt: "+ msg +"\n"); } :
   function log_noop(msg) { };
 
-const APPLY_PROMPT_TIMEOUT =
-      Services.prefs.getIntPref("b2g.update.apply-prompt-timeout");
-const APPLY_IDLE_TIMEOUT =
-      Services.prefs.getIntPref("b2g.update.apply-idle-timeout");
-const SELF_DESTRUCT_TIMEOUT =
-      Services.prefs.getIntPref("b2g.update.self-destruct-timeout");
+const PREF_APPLY_PROMPT_TIMEOUT = "b2g.update.apply-prompt-timeout";
+const PREF_APPLY_IDLE_TIMEOUT   = "b2g.update.apply-idle-timeout";
 
-const APPLY_IDLE_TIMEOUT_SECONDS = APPLY_IDLE_TIMEOUT / 1000;
 const NETWORK_ERROR_OFFLINE = 111;
 
 XPCOMUtils.defineLazyServiceGetter(Services, "aus",
                                    "@mozilla.org/updates/update-service;1",
                                    "nsIApplicationUpdateService");
 
 XPCOMUtils.defineLazyServiceGetter(Services, "um",
                                    "@mozilla.org/updates/update-manager;1",
@@ -103,16 +98,24 @@ UpdatePrompt.prototype = {
                                          Ci.nsIObserver]),
   _xpcom_factory: XPCOMUtils.generateSingletonFactory(UpdatePrompt),
 
   _update: null,
   _applyPromptTimer: null,
   _waitingForIdle: false,
   _updateCheckListner: null,
 
+  get applyPromptTimeout() {
+    return Services.prefs.getIntPref(PREF_APPLY_PROMPT_TIMEOUT);
+  },
+
+  get applyIdleTimeout() {
+    return Services.prefs.getIntPref(PREF_APPLY_IDLE_TIMEOUT);
+  },
+
   // nsIUpdatePrompt
 
   // FIXME/bug 737601: we should have users opt-in to downloading
   // updates when on a billed pipe.  Initially, opt-in for 3g, but
   // that doesn't cover all cases.
   checkForUpdates: function UP_checkForUpdates() { },
 
   showUpdateAvailable: function UP_showUpdateAvailable(aUpdate) {
@@ -125,24 +128,25 @@ UpdatePrompt.prototype = {
 
   showUpdateDownloaded: function UP_showUpdateDownloaded(aUpdate, aBackground) {
     // The update has been downloaded and staged. We send the update-downloaded
     // event right away. After the user has been idle for a while, we send the
     // update-prompt-restart event, increasing the chances that we can apply the
     // update quietly without user intervention.
     this.sendUpdateEvent("update-downloaded", aUpdate);
 
-    if (Services.idle.idleTime >= APPLY_IDLE_TIMEOUT) {
+    if (Services.idle.idleTime >= this.applyIdleTimeout) {
       this.showApplyPrompt(aUpdate);
       return;
     }
 
+    let applyIdleTimeoutSeconds = this.applyIdleTimeout / 1000;
     // We haven't been idle long enough, so register an observer
     log("Update is ready to apply, registering idle timeout of " +
-        APPLY_IDLE_TIMEOUT_SECONDS + " seconds before prompting.");
+        applyIdleTimeoutSeconds + " seconds before prompting.");
 
     this._update = aUpdate;
     this.waitForIdle();
   },
 
   showUpdateError: function UP_showUpdateError(aUpdate) {
     log("Update error, state: " + aUpdate.state + ", errorCode: " +
         aUpdate.errorCode);
@@ -160,17 +164,17 @@ UpdatePrompt.prototype = {
   // Custom functions
 
   waitForIdle: function UP_waitForIdle() {
     if (this._waitingForIdle) {
       return;
     }
 
     this._waitingForIdle = true;
-    Services.idle.addIdleObserver(this, APPLY_IDLE_TIMEOUT_SECONDS);
+    Services.idle.addIdleObserver(this, this.applyIdleTimeout / 1000);
     Services.obs.addObserver(this, "quit-application", false);
   },
 
   setUpdateStatus: function UP_setUpdateStatus(aStatus) {
     log("Setting gecko.updateStatus: " + aStatus);
 
     let lock = Services.settings.createLock();
     lock.set("gecko.updateStatus", aStatus, null);
@@ -180,28 +184,28 @@ UpdatePrompt.prototype = {
     if (!this.sendUpdateEvent("update-prompt-apply", aUpdate)) {
       log("Unable to prompt, forcing restart");
       this.restartProcess();
       return;
     }
 
     // Schedule a fallback timeout in case the UI is unable to respond or show
     // a prompt for some reason.
-    this._applyPromptTimer = this.createTimer(APPLY_PROMPT_TIMEOUT);
+    this._applyPromptTimer = this.createTimer(this.applyPromptTimeout);
   },
 
+  _copyProperties: ["appVersion", "buildID", "detailsURL", "displayVersion",
+                    "errorCode", "isOSUpdate", "platformVersion",
+                    "previousAppVersion", "state", "statusText"],
+
   sendUpdateEvent: function UP_sendUpdateEvent(aType, aUpdate) {
-    let detail = {
-      displayVersion: aUpdate.displayVersion,
-      detailsURL: aUpdate.detailsURL,
-      statusText: aUpdate.statusText,
-      state: aUpdate.state,
-      errorCode: aUpdate.errorCode,
-      isOSUpdate: aUpdate.isOSUpdate
-    };
+    let detail = {};
+    for each (let property in this._copyProperties) {
+      detail[property] = aUpdate[property];
+    }
 
     let patch = aUpdate.selectedPatch;
     if (!patch && aUpdate.patchCount > 0) {
       // For now we just check the first patch to get size information if a
       // patch hasn't been selected yet.
       patch = aUpdate.getPatchAt(0);
     }
 
@@ -424,17 +428,17 @@ UpdatePrompt.prototype = {
 
   observe: function UP_observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "idle":
         this._waitingForIdle = false;
         this.showApplyPrompt(this._update);
         // Fall through
       case "quit-application":
-        Services.idle.removeIdleObserver(this, APPLY_IDLE_TIMEOUT_SECONDS);
+        Services.idle.removeIdleObserver(this, this.applyIdleTimeout / 1000);
         Services.obs.removeObserver(this, "quit-application");
         break;
       case "update-check-start":
         this.onUpdateCheckStart();
         break;
     }
   },
 
--- a/b2g/components/YoutubeProtocolHandler.js
+++ b/b2g/components/YoutubeProtocolHandler.js
@@ -11,27 +11,16 @@ const Cu = Components.utils;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
   return Cc["@mozilla.org/childprocessmessagemanager;1"]
            .getService(Ci.nsIMessageSender);
 });
 
-// Splits parameters in a query string.
-function extractParameters(aQuery) {
-  let params = aQuery.split("&");
-  let res = {};
-  params.forEach(function(aParam) {
-    let obj = aParam.split("=");
-    res[obj[0]] = decodeURIComponent(obj[1]);
-  });
-  return res;
-}
-
 function YoutubeProtocolHandler() {
 }
 
 YoutubeProtocolHandler.prototype = {
   classID: Components.ID("{c3f1b945-7e71-49c8-95c7-5ae9cc9e2bad}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),
 
   scheme: "vnd.youtube",
@@ -56,75 +45,146 @@ YoutubeProtocolHandler.prototype = {
     // Get a list of streams for this video id.
     let infoURI = "http://www.youtube.com/get_video_info?&video_id=" +
                   aURI.path.substring(1);
 
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
                 .createInstance(Ci.nsIXMLHttpRequest);
     xhr.open("GET", infoURI, true);
     xhr.addEventListener("load", function() {
-      // Youtube sends the response as a double wrapped url answer:
-      // we first extract the url_encoded_fmt_stream_map parameter,
-      // and from each comma-separated entry in this value, we extract
-      // other parameters (url and type).
-      let key = "url_encoded_fmt_stream_map=";
-      let pos = xhr.responseText.indexOf(key);
-      if (pos == -1) {
-        return;
+      try {
+        let info = parseYoutubeVideoInfo(xhr.responseText);
+        cpmm.sendAsyncMessage("content-handler", info);
+      }
+      catch(e) {
+        // If parseYoutubeVideoInfo() can't find a video URL, it
+        // throws an Error. We report the error message here and do
+        // nothing. This shouldn't happen often. But if it does, the user
+        // will find that clicking on a video doesn't do anything.
+        log(e.message);
+      }
+    });
+    xhr.send(null);
+
+    function log(msg) {
+      msg = "YoutubeProtocolHandler.js: " + (msg.join ? msg.join(" ") : msg);
+      Cc["@mozilla.org/consoleservice;1"]
+        .getService(Ci.nsIConsoleService)
+        .logStringMessage(msg);
+    }
+
+    // 
+    // Parse the response from a youtube get_video_info query.
+    // 
+    // If youtube's response is a failure, this function returns an object
+    // with status, errorcode, type and reason properties. Otherwise, it returns
+    // an object with status, url, and type properties, and optional
+    // title, poster, and duration properties.
+    // 
+    function parseYoutubeVideoInfo(response) {
+      // Splits parameters in a query string.
+      function extractParameters(q) {
+        let params = q.split("&");
+        let result = {};
+        for(let i = 0, n = params.length; i < n; i++) {
+          let param = params[i];
+          let pos = param.indexOf('=');
+          if (pos === -1) 
+            continue;
+          let name = param.substring(0, pos);
+          let value = param.substring(pos+1);
+          result[name] = decodeURIComponent(value);
+        }
+        return result;
       }
-      let streams = decodeURIComponent(xhr.responseText
-                                       .substring(pos + key.length)).split(",");
-      let uri;
-      let mimeType;
 
-      // itag is an undocumented value which maps to resolution and mimetype
-      // see https://en.wikipedia.org/wiki/YouTube#Quality_and_codecs
-      // Ordered from least to most preferred
-      let recognizedItags = [
+      let params = extractParameters(response);
+      
+      // If the request failed, return an object with an error code
+      // and an error message
+      if (params.status === 'fail') {
+        // 
+        // Hopefully this error message will be properly localized.
+        // Do we need to add any parameters to the XMLHttpRequest to 
+        // specify the language we want?
+        // 
+        // Note that we include fake type and url properties in the returned
+        // object. This is because we still need to trigger the video app's
+        // view activity handler to display the error message from youtube,
+        // and those parameters are required.
+        //
+        return {
+          status: params.status,
+          errorcode: params.errorcode,
+          reason:  (params.reason || '').replace(/\+/g, ' '),
+          type: 'video/3gpp',
+          url: 'https://m.youtube.com'
+        }
+      }
+
+      // Otherwise, the query was successful
+      let result = {
+        status: params.status,
+      };
+
+      // Now parse the available streams
+      let streamsText = params.url_encoded_fmt_stream_map;
+      if (!streamsText)
+        throw Error("No url_encoded_fmt_stream_map parameter");
+      let streams = streamsText.split(',');
+      for(let i = 0, n = streams.length; i < n; i++) {
+        streams[i] = extractParameters(streams[i]);
+      }
+
+      // This is the list of youtube video formats, ordered from worst
+      // (but playable) to best.  These numbers are values used as the
+      // itag parameter of each stream description. See
+      // https://en.wikipedia.org/wiki/YouTube#Quality_and_codecs
+      let formats = [
         "17", // 144p 3GP
         "36", // 240p 3GP
         "43", // 360p WebM
 #ifdef MOZ_WIDGET_GONK
         "18", // 360p H.264
 #endif
       ];
 
-      let bestItag = -1;
-
-      let extras = { }
-
-      streams.forEach(function(aStream) {
-        let params = extractParameters(aStream);
-        let url = params["url"];
-        let type = params["type"] ? params["type"].split(";")[0] : null;
-        let itag = params["itag"];
-
-        let index;
-        if (url && type && ((index = recognizedItags.indexOf(itag)) != -1) &&
-            index > bestItag) {
-          uri = url + '&signature=' + (params["sig"] ? params['sig'] : '');
-          mimeType = type;
-          bestItag = index;
-        }
-        for (let param in params) {
-          if (["thumbnail_url", "length_seconds", "title"].indexOf(param) != -1) {
-            extras[param] = decodeURIComponent(params[param]);
-          }
-        }
+      // Sort the array of stream descriptions in order of format
+      // preference, so that the first item is the most preferred one
+      streams.sort(function(a, b) {
+        let x = a.itag ? formats.indexOf(a.itag) : -1;
+        let y = b.itag ? formats.indexOf(b.itag) : -1;
+        return y - x;
       });
 
-      if (uri && mimeType) {
-        cpmm.sendAsyncMessage("content-handler", {
-          url: uri,
-          type: mimeType,
-          extras: extras
-        });
+      let bestStream = streams[0];
+
+      // If the best stream is a format we don't support just return
+      if (formats.indexOf(bestStream.itag) === -1) 
+        throw Error("No supported video formats");
+
+      result.url = bestStream.url + '&signature=' + (bestStream.sig || '');
+      result.type = bestStream.type;
+      // Strip codec information off of the mime type
+      if (result.type && result.type.indexOf(';') !== -1) {
+        result.type = result.type.split(';',1)[0];
       }
-    });
-    xhr.send(null);
+
+      if (params.title) {
+        result.title = params.title.replace(/\+/g, ' ');
+      }
+      if (params.length_seconds) {
+        result.duration = params.length_seconds;
+      }
+      if (params.thumbnail_url) {
+        result.poster = params.thumbnail_url;
+      }
+
+      return result;
+    }
 
     throw Components.results.NS_ERROR_ILLEGAL_VALUE;
   },
 
   allowPort: function yt_phAllowPort(aPort, aScheme) {
     return false;
   }
 };
--- a/b2g/components/test/unit/test_signintowebsite.js
+++ b/b2g/components/test/unit/test_signintowebsite.js
@@ -201,17 +201,17 @@ function test_request_login_logout() {
 function test_options_pass_through() {
   do_test_pending();
 
   // An meaningless structure for testing that RP messages preserve
   // objects and their parameters as they are passed back and forth.
   let randomMixedParams = {
     loggedInUser: "juanita@mozilla.com",
     forceAuthentication: true,
-    issuer: "https://foo.com",
+    forceIssuer: "foo.com",
     someThing: {
       name: "Pertelote",
       legs: 4,
       nested: {bee: "Eric", remaining: "1/2"}
       }
     };
 
   let mockedDoc = mockDoc(randomMixedParams, function(action, params) {});
--- a/b2g/config/tooltool-manifests/macosx64/releng.manifest
+++ b/b2g/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,17 +1,17 @@
 [
 {
-"clang_version": "r170377"
+"clang_version": "r170890"
 },
 {
 "size": 47,
 "digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
 "algorithm": "sha512",
 "filename": "setup.sh"
 },
 {
-"size": 56131193,
-"digest": "a1e705d3a72e0e95eeb15722f538a3908277f9f756909ce5e67f0a09b8531c1ba7fcc4816e20795af2e98839e0308b1bc6df95308cda310ae06abf21d429624f",
+"size": 56126352,
+"digest": "e156e2a39abd5bf272ee30748a6825f22ddd27565b097c66662a2a6f2e9892bc5b4bf30a3552dffbe867dbfc39e7ee086e0b2cd7935f6ea216c0cf936178a88f",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2"
 }
 ]
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -153,16 +153,17 @@
 @BINPATH@/components/content_xslt.xpt
 @BINPATH@/components/cookie.xpt
 @BINPATH@/components/directory.xpt
 @BINPATH@/components/docshell.xpt
 @BINPATH@/components/dom.xpt
 @BINPATH@/components/dom_activities.xpt
 @BINPATH@/components/dom_apps.xpt
 @BINPATH@/components/dom_base.xpt
+@BINPATH@/components/dom_system.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_telephony.xpt
 @BINPATH@/components/dom_wifi.xpt
 @BINPATH@/components/dom_system_gonk.xpt
 @BINPATH@/components/dom_icc.xpt
 @BINPATH@/components/dom_cellbroadcast.xpt
 #endif
 #ifdef MOZ_B2G_FM
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -239,17 +239,16 @@ pref("browser.startup.homepage",        
 // repackager of this code using an alternate snippet url, please keep your users safe
 pref("browser.aboutHomeSnippets.updateUrl", "https://snippets.mozilla.com/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/");
 
 pref("browser.enable_automatic_image_resizing", true);
 pref("browser.chrome.site_icons", true);
 pref("browser.chrome.favicons", true);
 // browser.warnOnQuit == false will override all other possible prompts when quitting or restarting
 pref("browser.warnOnQuit", true);
-pref("browser.warnOnRestart", false);
 // browser.showQuitWarning specifically controls the quit warning dialog. We
 // might still show the window closing dialog with showQuitWarning == false.
 pref("browser.showQuitWarning", false);
 pref("browser.fullscreen.autohide", true);
 pref("browser.fullscreen.animateUp", 1);
 pref("browser.overlink-delay", 80);
 
 #ifdef UNIX_BUT_NOT_MAC
@@ -736,17 +735,17 @@ pref("urlclassifier.gethashnoise", 4);
 pref("urlclassifier.randomizeclient", false);
 
 // The list of tables that use the gethash request to confirm partial results.
 pref("urlclassifier.gethashtables", "goog-phish-shavar,goog-malware-shavar");
 
 // If an urlclassifier table has not been updated in this number of seconds,
 // a gethash request will be forced to check that the result is still in
 // the database.
-pref("urlclassifier.confirm-age", 2700);
+pref("urlclassifier.max-complete-age", 2700);
 #endif
 
 pref("browser.geolocation.warning.infoURL", "http://www.mozilla.com/%LOCALE%/firefox/geolocation/");
 
 pref("browser.EULA.version", 3);
 pref("browser.rights.version", 3);
 pref("browser.rights.3.shown", false);
 
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -1,15 +1,19 @@
 # -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 # 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/.
 
 var FullScreen = {
   _XULNS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+  get _fullScrToggler() {
+    delete this._fullScrToggler;
+    return this._fullScrToggler = document.getElementById("fullscr-toggler");
+  },
   toggle: function (event) {
     var enterFS = window.fullScreen;
 
     // We get the fullscreen event _before_ the window transitions into or out of FS mode.
     if (event && event.type == "fullscreen")
       enterFS = !enterFS;
 
     // Toggle the View:FullScreen command, which controls elements like the
@@ -41,25 +45,18 @@ var FullScreen = {
     this.showXULChrome("toolbar", !enterFS);
 
     if (enterFS) {
       // Add a tiny toolbar to receive mouseover and dragenter events, and provide affordance.
       // This will help simulate the "collapse" metaphor while also requiring less code and
       // events than raw listening of mouse coords. We don't add the toolbar in DOM full-screen
       // mode, only browser full-screen mode.
       if (!document.mozFullScreen) {
-        let fullScrToggler = document.getElementById("fullscr-toggler");
-        if (!fullScrToggler) {
-          fullScrToggler = document.createElement("hbox");
-          fullScrToggler.id = "fullscr-toggler";
-          fullScrToggler.collapsed = true;
-          gNavToolbox.parentNode.insertBefore(fullScrToggler, gNavToolbox.nextSibling);
-        }
-        fullScrToggler.addEventListener("mouseover", this._expandCallback, false);
-        fullScrToggler.addEventListener("dragenter", this._expandCallback, false);
+        this._fullScrToggler.addEventListener("mouseover", this._expandCallback, false);
+        this._fullScrToggler.addEventListener("dragenter", this._expandCallback, false);
       }
       if (gPrefService.getBoolPref("browser.fullscreen.autohide"))
         gBrowser.mPanelContainer.addEventListener("mousemove",
                                                   this._collapseCallback, false);
 
       document.addEventListener("keypress", this._keyToggleCallback, false);
       document.addEventListener("popupshown", this._setPopupOpen, false);
       document.addEventListener("popuphidden", this._setPopupOpen, false);
@@ -147,39 +144,33 @@ var FullScreen = {
       window.addEventListener("deactivate", this);
     }
 
     // Cancel any "hide the toolbar" animation which is in progress, and make
     // the toolbar hide immediately.
     this._cancelAnimation();
     this.mouseoverToggle(false);
 
-    // If there's a full-screen toggler, remove its listeners, so that mouseover
+    // Remove listeners on the full-screen toggler, so that mouseover
     // the top of the screen will not cause the toolbar to re-appear.
-    let fullScrToggler = document.getElementById("fullscr-toggler");
-    if (fullScrToggler) {
-      fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
-      fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
-    }
+    this._fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
+    this._fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
   },
 
   cleanup: function () {
     if (window.fullScreen) {
       gBrowser.mPanelContainer.removeEventListener("mousemove",
                                                    this._collapseCallback, false);
       document.removeEventListener("keypress", this._keyToggleCallback, false);
       document.removeEventListener("popupshown", this._setPopupOpen, false);
       document.removeEventListener("popuphidden", this._setPopupOpen, false);
       gPrefService.removeObserver("browser.fullscreen", this);
 
-      let fullScrToggler = document.getElementById("fullscr-toggler");
-      if (fullScrToggler) {
-        fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
-        fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
-      }
+      this._fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
+      this._fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
       this.cancelWarning();
       gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen);
       gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen);
       gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
       if (!this.useLionFullScreen)
         window.removeEventListener("deactivate", this);
     }
   },
@@ -501,20 +492,17 @@ var FullScreen = {
                                                    this._collapseCallback, false);
     }
 
     // Hiding/collapsing the toolbox interferes with the tab bar's scrollbox,
     // so we just move it off-screen instead. See bug 430687.
     gNavToolbox.style.marginTop =
       aShow ? "" : -gNavToolbox.getBoundingClientRect().height + "px";
 
-    let toggler = document.getElementById("fullscr-toggler");
-    if (toggler) {
-      toggler.collapsed = aShow;
-    }
+    this._fullScrToggler.collapsed = aShow;
     this._isChromeCollapsed = !aShow;
     if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 2)
       this._shouldAnimate = true;
   },
 
   showXULChrome: function(aTag, aShow)
   {
     var els = document.getElementsByTagNameNS(this._XULNS, aTag);
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -264,17 +264,21 @@ var PlacesCommandHook = {
       // no DOMWindow (?) but information about the loaded document
       // may still be obtained from the webNavigation.
       var webNav = aBrowser.webNavigation;
       var url = webNav.currentURI;
       var title;
       var description;
       var charset;
       try {
-        title = webNav.document.title || url.spec;
+        let isErrorPage = /^about:(neterror|certerror|blocked)/
+                          .test(webNav.document.documentURI);
+        title = isErrorPage ? PlacesUtils.history.getPageTitle(url)
+                            : webNav.document.title;
+        title = title || url.spec;
         description = PlacesUIUtils.getDescriptionFromDocument(webNav.document);
         charset = webNav.document.characterSet;
       }
       catch (e) { }
 
       if (aShowEditUI) {
         // If we bookmark the page here (i.e. page was not "starred" already)
         // but open right into the "edit" state, start batching here, so
--- a/browser/base/content/browser-tabPreviews.js
+++ b/browser/base/content/browser-tabPreviews.js
@@ -443,17 +443,17 @@ var ctrlTab = {
             this.advanceFocus(!event.shiftKey);
           } else if (!event.shiftKey) {
             event.preventDefault();
             event.stopPropagation();
             let tabs = gBrowser.visibleTabs;
             if (tabs.length > 2) {
               this.open();
             } else if (tabs.length == 2) {
-              let index = gBrowser.selectedTab == tabs[0] ? 1 : 0;
+              let index = tabs[0].selected ? 1 : 0;
               gBrowser.selectedTab = tabs[index];
             }
           }
         }
         break;
       default:
         if (isOpen && event.ctrlKey) {
           if (event.keyCode == event.DOM_VK_DELETE) {
@@ -483,17 +483,17 @@ var ctrlTab = {
     this.updatePreviews();
 
     if (this.selected.hidden)
       this.advanceFocus(false);
     if (this.selected == this.showAllButton)
       this.advanceFocus(false);
 
     // If the current tab is removed, another tab can steal our focus.
-    if (aTab == gBrowser.selectedTab && this.panel.state == "open") {
+    if (aTab.selected && this.panel.state == "open") {
       setTimeout(function (selected) {
         selected.focus();
       }, 0, this.selected);
     }
   },
 
   handleEvent: function ctrlTab_handleEvent(event) {
     switch (event.type) {
new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-webrtcUI.js
@@ -0,0 +1,52 @@
+# -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+# 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/.
+
+let WebrtcIndicator = {
+  init: function () {
+    let temp = {};
+    Cu.import("resource:///modules/webrtcUI.jsm", temp);
+    this.UIModule = temp.webrtcUI;
+
+    this.updateButton();
+  },
+
+  get button() {
+    delete this.button;
+    return this.button = document.getElementById("webrtc-status-button");
+  },
+
+  updateButton: function () {
+    this.button.hidden = !this.UIModule.showGlobalIndicator;
+  },
+
+  fillPopup: function (aPopup) {
+    this._menuitemData = new WeakMap;
+    for (let streamData of this.UIModule.activeStreams) {
+      let menuitem = document.createElement("menuitem");
+      menuitem.setAttribute("label", streamData.uri);
+      menuitem.setAttribute("tooltiptext", streamData.uri);
+
+      this._menuitemData.set(menuitem, streamData);
+
+      aPopup.appendChild(menuitem);
+    }
+  },
+
+  clearPopup: function (aPopup) {
+    while (aPopup.lastChild)
+      aPopup.removeChild(aPopup.lastChild);
+  },
+
+  menuCommand: function (aMenuitem) {
+    let streamData = this._menuitemData.get(aMenuitem);
+    if (!streamData)
+      return;
+
+    let tab = streamData.tab;
+    let browserWindow = tab.ownerDocument.defaultView;
+    browserWindow.gBrowser.selectedTab = tab;
+    browserWindow.focus();
+  }
+}
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -471,20 +471,16 @@ window[chromehidden~="toolbar"] toolbar:
   -moz-binding: url("chrome://global/content/bindings/text.xml#text-label");
   text-decoration: none;
 }
 
 #invalid-form-popup > description {
   max-width: 280px;
 }
 
-#geolocation-notification {
-  -moz-binding: url("chrome://browser/content/urlbarBindings.xml#geolocation-notification");
-}
-
 #addon-progress-notification {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#addon-progress-notification");
 }
 
 #identity-request-notification {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#identity-request-notification");
 }
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -150,16 +150,17 @@ let gInitialPages = [
 #include browser-fullZoom.js
 #include browser-places.js
 #include browser-plugins.js
 #include browser-safebrowsing.js
 #include browser-social.js
 #include browser-tabPreviews.js
 #include browser-tabview.js
 #include browser-thumbnails.js
+#include browser-webrtcUI.js
 
 #ifdef MOZ_SERVICES_SYNC
 #include browser-syncui.js
 #endif
 
 XPCOMUtils.defineLazyGetter(this, "Win7Features", function () {
 #ifdef XP_WIN
   const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
@@ -1249,25 +1250,26 @@ var gBrowserInit = {
     Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
 
     BrowserOffline.init();
     OfflineApps.init();
     IndexedDBPromptHelper.init();
     gFormSubmitObserver.init();
     SocialUI.init();
     AddonManager.addAddonListener(AddonsMgrListener);
+    WebrtcIndicator.init();
 
     gBrowser.addEventListener("pageshow", function(event) {
       // Filter out events that are not about the document load we are interested in
       if (event.target == content.document)
         setTimeout(pageShowEventHandlers, 0, event);
     }, true);
 
     // Ensure login manager is up and running.
-    Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
+    Services.logins;
 
     if (mustLoadSidebar) {
       let sidebar = document.getElementById("sidebar");
       let sidebarBox = document.getElementById("sidebar-box");
       sidebar.setAttribute("src", sidebarBox.getAttribute("src"));
     }
 
     UpdateUrlbarSearchSplitterState();
@@ -1336,17 +1338,17 @@ var gBrowserInit = {
     gPrefService.addObserver(allTabs.prefName, allTabs, false);
 
     // Initialize the download manager some time after the app starts so that
     // auto-resume downloads begin (such as after crashing or quitting with
     // active downloads) and speeds up the first-load of the download manager UI.
     // If the user manually opens the download manager before the timeout, the
     // downloads will start right away, and getting the service again won't hurt.
     setTimeout(function() {
-      Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
+      Services.downloads;
 
 #ifdef XP_WIN
       if (Win7Features) {
         let tempScope = {};
         Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm",
                   tempScope);
         tempScope.DownloadTaskbarProgress.onBrowserWindowLoad(window);
       }
@@ -1490,20 +1492,18 @@ var gBrowserInit = {
     }
 
     window.addEventListener("mousemove", MousePosTracker, false);
     window.addEventListener("dragover", MousePosTracker, false);
 
     // End startup crash tracking after a delay to catch crashes while restoring
     // tabs and to postpone saving the pref to disk.
     try {
-      let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].
-                       getService(Ci.nsIAppStartup);
       const startupCrashEndDelay = 30 * 1000;
-      setTimeout(appStartup.trackStartupCrashEnd, startupCrashEndDelay);
+      setTimeout(Services.startup.trackStartupCrashEnd, startupCrashEndDelay);
     } catch (ex) {
       Cu.reportError("Could not end startup crash tracking: " + ex);
     }
 
     Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
     TelemetryTimestamps.add("delayedStartupFinished");
   },
 
@@ -1745,17 +1745,16 @@ var BrowserShutdown       = gBrowserInit
 #ifdef XP_MACOSX
 var nonBrowserWindowStartup        = gBrowserInit.nonBrowserWindowStartup.bind(gBrowserInit);
 var nonBrowserWindowDelayedStartup = gBrowserInit.nonBrowserWindowDelayedStartup.bind(gBrowserInit);
 var nonBrowserWindowShutdown       = gBrowserInit.nonBrowserWindowShutdown.bind(gBrowserInit);
 #endif
 
 
 function HandleAppCommandEvent(evt) {
-  evt.stopPropagation();
   switch (evt.command) {
   case "Back":
     BrowserBack();
     break;
   case "Forward":
     BrowserForward();
     break;
   case "Reload":
@@ -1769,19 +1768,45 @@ function HandleAppCommandEvent(evt) {
     BrowserSearch.webSearch();
     break;
   case "Bookmarks":
     toggleSidebar('viewBookmarksSidebar');
     break;
   case "Home":
     BrowserHome();
     break;
-  default:
+  case "New":
+    BrowserOpenTab();
+    break;
+  case "Close":
+    BrowserCloseTabOrWindow();
+    break;
+  case "Find":
+    gFindBar.onFindCommand();
+    break;
+  case "Help":
+    openHelpLink('firefox-help');
+    break;
+  case "Open":
+    BrowserOpenFileWindow();
     break;
+  case "Print":
+    PrintUtils.print();
+    break;
+  case "Save":
+    saveDocument(window.content.document);
+    break;
+  case "SendMail":
+    MailIntegration.sendLinkForWindow(window.content);
+    break;
+  default:
+    return;
   }
+  evt.stopPropagation();
+  evt.preventDefault();
 }
 
 function gotoHistoryIndex(aEvent) {
   let index = aEvent.target.getAttribute("index");
   if (!index)
     return false;
 
   let where = whereToOpenLink(aEvent);
@@ -2217,32 +2242,28 @@ function getLoadContext() {
                .QueryInterface(Ci.nsILoadContext);
 }
 
 function readFromClipboard()
 {
   var url;
 
   try {
-    // Get clipboard.
-    var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"]
-                              .getService(Components.interfaces.nsIClipboard);
-
     // Create transferable that will transfer the text.
     var trans = Components.classes["@mozilla.org/widget/transferable;1"]
                           .createInstance(Components.interfaces.nsITransferable);
     trans.init(getLoadContext());
 
     trans.addDataFlavor("text/unicode");
 
     // If available, use selection clipboard, otherwise global one
-    if (clipboard.supportsSelectionClipboard())
-      clipboard.getData(trans, clipboard.kSelectionClipboard);
+    if (Services.clipboard.supportsSelectionClipboard())
+      Services.clipboard.getData(trans, Services.clipboard.kSelectionClipboard);
     else
-      clipboard.getData(trans, clipboard.kGlobalClipboard);
+      Services.clipboard.getData(trans, Services.clipboard.kGlobalClipboard);
 
     var data = {};
     var dataLen = {};
     trans.getTransferData("text/unicode", data, dataLen);
 
     if (data) {
       data = data.value.QueryInterface(Components.interfaces.nsISupportsString);
       url = data.data.substring(0, dataLen.value / 2);
@@ -2298,19 +2319,17 @@ function BrowserViewSourceOfDocument(aDo
   top.gViewSourceUtils.viewSource(webNav.currentURI.spec, pageCookie, aDocument);
 }
 
 // doc - document to use for source, or null for this window's document
 // initialTab - name of the initial tab to display, or null for the first tab
 // imageElement - image to load in the Media Tab of the Page Info window; can be null/omitted
 function BrowserPageInfo(doc, initialTab, imageElement) {
   var args = {doc: doc, initialTab: initialTab, imageElement: imageElement};
-  var windows = Cc['@mozilla.org/appshell/window-mediator;1']
-                  .getService(Ci.nsIWindowMediator)
-                  .getEnumerator("Browser:page-info");
+  var windows = Services.wm.getEnumerator("Browser:page-info");
 
   var documentURL = doc ? doc.location : window.content.document.location;
 
   // Check for windows matching the url
   while (windows.hasMoreElements()) {
     var currentWindow = windows.getNext();
     if (currentWindow.document.documentElement.getAttribute("relatedUrl") == documentURL) {
       currentWindow.focus();
@@ -2463,17 +2482,17 @@ function PageProxyClickHandler(aEvent)
     middleMousePaste(aEvent);
 }
 
 /**
  *  Handle load of some pages (about:*) so that we can make modifications
  *  to the DOM for unprivileged pages.
  */
 function BrowserOnAboutPageLoad(document) {
-  if (/^about:home$/i.test(document.documentURI)) {
+  if (document.documentURI.toLowerCase() == "about:home") {
     // XXX bug 738646 - when Marketplace is launched, remove this statement and
     // the hidden attribute set on the apps button in aboutHome.xhtml
     if (getBoolPref("browser.aboutHome.apps", false))
       document.getElementById("apps").removeAttribute("hidden");
 
     let ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
              getService(Components.interfaces.nsISessionStore);
     if (ss.canRestoreLastSession
@@ -2512,26 +2531,24 @@ let BrowserOnClick = {
       this.onAboutCertError(originalTarget, ownerDoc);
     }
     else if (ownerDoc.documentURI.startsWith("about:blocked")) {
       this.onAboutBlocked(originalTarget, ownerDoc);
     }
     else if (ownerDoc.documentURI.startsWith("about:neterror")) {
       this.onAboutNetError(originalTarget, ownerDoc);
     }
-    else if (/^about:home$/i.test(ownerDoc.documentURI)) {
+    else if (ownerDoc.documentURI.toLowerCase() == "about:home") {
       this.onAboutHome(originalTarget, ownerDoc);
     }
   },
 
   onAboutCertError: function BrowserOnClick_onAboutCertError(aTargetElm, aOwnerDoc) {
     let elmId = aTargetElm.getAttribute("id");
-    let secHistogram = Cc["@mozilla.org/base/telemetry;1"].
-                        getService(Ci.nsITelemetry).
-                        getHistogramById("SECURITY_UI");
+    let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
 
     switch (elmId) {
       case "exceptionDialogButton":
         secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_CLICK_ADD_EXCEPTION);
         let params = { exceptionAdded : false };
 
         try {
           switch (Services.prefs.getIntPref("browser.ssl_override_behavior")) {
@@ -2566,19 +2583,17 @@ let BrowserOnClick = {
         secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_UNDERSTAND_RISKS);
         break;
 
     }
   },
 
   onAboutBlocked: function BrowserOnClick_onAboutBlocked(aTargetElm, aOwnerDoc) {
     let elmId = aTargetElm.getAttribute("id");
-    let secHistogram = Cc["@mozilla.org/base/telemetry;1"].
-                        getService(Ci.nsITelemetry).
-                        getHistogramById("SECURITY_UI");
+    let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
 
     // The event came from a button on a malware/phishing block page
     // First check whether it's malware or phishing, so that we can
     // use the right strings/links
     let isMalware = /e=malwareBlocked/.test(aOwnerDoc.documentURI);
     let bucketName = isMalware ? "WARNING_MALWARE_PAGE_":"WARNING_PHISHING_PAGE_";
     let nsISecTel = Ci.nsISecurityUITelemetry;
 
@@ -2738,18 +2753,17 @@ let BrowserOnClick = {
  * Re-direct the browser to a known-safe page.  This function is
  * used when, for example, the user browses to a known malware page
  * and is presented with about:blocked.  The "Get me out of here!"
  * button should take the user to the default start page so that even
  * when their own homepage is infected, we can get them somewhere safe.
  */
 function getMeOutOfHere() {
   // Get the start page from the *default* pref branch, not the user's
-  var prefs = Cc["@mozilla.org/preferences-service;1"]
-             .getService(Ci.nsIPrefService).getDefaultBranch(null);
+  var prefs = Services.prefs.getDefaultBranch(null);
   var url = BROWSER_NEW_TAB_URL;
   try {
     url = prefs.getComplexValue("browser.startup.homepage",
                                 Ci.nsIPrefLocalizedString).data;
     // If url is a pipe-delimited set of pages, just take the first one.
     if (url.contains("|"))
       url = url.split("|")[0];
   } catch(e) {
@@ -3129,18 +3143,17 @@ const DOMLinkHandler = {
     var isAllowedPage = [
       /^about:neterror\?/,
       /^about:blocked\?/,
       /^about:certerror\?/,
       /^about:home$/,
     ].some(function (re) re.test(targetDoc.documentURI));
 
     if (!isAllowedPage || !uri.schemeIs("chrome")) {
-      var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"].
-                getService(Ci.nsIScriptSecurityManager);
+      var ssm = Services.scriptSecurityManager;
       try {
         ssm.checkLoadURIWithPrincipal(targetDoc.nodePrincipal, uri,
                                       Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
       } catch(e) {
         return null;
       }
     }
 
@@ -3763,34 +3776,35 @@ function updateEditUIVisibility()
  * @param aMimeType
  *        The MIME type to check.
  *
  * If adding types to this function, please also check the similar
  * function in findbar.xml
  */
 function mimeTypeIsTextBased(aMimeType)
 {
-  return /^text\/|\+xml$/.test(aMimeType) ||
+  return aMimeType.startsWith("text/") ||
+         aMimeType.endsWith("+xml") ||
          aMimeType == "application/x-javascript" ||
          aMimeType == "application/javascript" ||
          aMimeType == "application/xml" ||
          aMimeType == "mozilla.application/cached-xul";
 }
 
 var XULBrowserWindow = {
   // Stored Status, Link and Loading values
   status: "",
   defaultStatus: "",
   jsStatus: "",
   jsDefaultStatus: "",
   overLink: "",
   startTime: 0,
   statusText: "",
   isBusy: false,
-  inContentWhitelist: ["about:addons", "about:permissions",
+  inContentWhitelist: ["about:addons", "about:downloads", "about:permissions",
                        "about:sync-progress", "about:preferences"],
 
   QueryInterface: function (aIID) {
     if (aIID.equals(Ci.nsIWebProgressListener) ||
         aIID.equals(Ci.nsIWebProgressListener2) ||
         aIID.equals(Ci.nsISupportsWeakReference) ||
         aIID.equals(Ci.nsIXULBrowserWindow) ||
         aIID.equals(Ci.nsISupports))
@@ -4061,17 +4075,17 @@ var XULBrowserWindow = {
     if (selectedBrowser.lastURI) {
       let oldSpec = selectedBrowser.lastURI.spec;
       let oldIndexOfHash = oldSpec.indexOf("#");
       if (oldIndexOfHash != -1)
         oldSpec = oldSpec.substr(0, oldIndexOfHash);
       let newSpec = location;
       let newIndexOfHash = newSpec.indexOf("#");
       if (newIndexOfHash != -1)
-        newSpec = newSpec.substr(0, newSpec.indexOf("#"));
+        newSpec = newSpec.substr(0, newIndexOfHash);
       if (newSpec != oldSpec) {
         // Remove all the notifications, except for those which want to
         // persist across the first location change.
         let nBox = gBrowser.getNotificationBox(selectedBrowser);
         nBox.removeTransientNotifications();
 
         // Only need to call locationChange if the PopupNotifications object
         // for this window has already been initialized (i.e. its getter no
@@ -5078,18 +5092,18 @@ var gHomeButton = {
     try {
       url = gPrefService.getComplexValue(this.prefDomain,
                                 Components.interfaces.nsIPrefLocalizedString).data;
     } catch (e) {
     }
 
     // use this if we can't find the pref
     if (!url) {
-      var SBS = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
-      var configBundle = SBS.createBundle("chrome://branding/locale/browserconfig.properties");
+      var configBundle = Services.strings
+                                 .createBundle("chrome://branding/locale/browserconfig.properties");
       url = configBundle.GetStringFromName(this.prefDomain);
     }
 
     return url;
   },
 
   updatePersonalToolbarStyle: function (homeButton)
   {
@@ -5139,19 +5153,17 @@ function getBrowserSelection(aCharLen) {
   if (selection) {
     if (selection.length > charLen) {
       // only use the first charLen important chars. see bug 221361
       var pattern = new RegExp("^(?:\\s*.){0," + charLen + "}");
       pattern.test(selection);
       selection = RegExp.lastMatch;
     }
 
-    selection = selection.replace(/^\s+/, "")
-                         .replace(/\s+$/, "")
-                         .replace(/\s+/g, " ");
+    selection = selection.trim().replace(/\s+/g, " ");
 
     if (selection.length > charLen)
       selection = selection.substr(0, charLen);
   }
   return selection;
 }
 
 var gWebPanelURI;
@@ -5273,18 +5285,18 @@ function contentAreaClick(event, isPanel
     // A Web panel's links should target the main content area.  Do this
     // if no modifier keys are down and if there's no target or the target
     // equals _main (the IE convention) or _content (the Mozilla convention).
     let target = linkNode.target;
     let mainTarget = !target || target == "_content" || target  == "_main";
     if (isPanelClick && mainTarget) {
       // javascript and data links should be executed in the current browser.
       if (linkNode.getAttribute("onclick") ||
-          href.substr(0, 11) === "javascript:" ||
-          href.substr(0, 5) === "data:")
+          href.startsWith("javascript:") ||
+          href.startsWith("data:"))
         return;
 
       try {
         urlSecurityCheck(href, linkNode.ownerDocument.nodePrincipal);
       }
       catch(ex) {
         // Prevent loading unsecure destinations.
         event.preventDefault();
@@ -6639,26 +6651,22 @@ var gIdentityHandler = {
     else
       this.setMode(this.IDENTITY_MODE_UNKNOWN);
   },
 
   /**
    * Return the eTLD+1 version of the current hostname
    */
   getEffectiveHost : function() {
-    // Cache the eTLDService if this is our first time through
-    if (!this._eTLDService)
-      this._eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"]
-                         .getService(Ci.nsIEffectiveTLDService);
     if (!this._IDNService)
       this._IDNService = Cc["@mozilla.org/network/idn-service;1"]
                          .getService(Ci.nsIIDNService);
     try {
       let baseDomain =
-        this._eTLDService.getBaseDomainFromHost(this._lastLocation.hostname);
+        Services.eTLD.getBaseDomainFromHost(this._lastLocation.hostname);
       return this._IDNService.convertToDisplayIDN(baseDomain, {});
     } catch (e) {
       // If something goes wrong (e.g. hostname is an IP address) just fail back
       // to the full domain.
       return this._lastLocation.hostname;
     }
   },
 
@@ -6995,18 +7003,17 @@ let gPrivateBrowsingUI = {
       // Never prompt if the session is not going to be closed, or if user has
       // already requested not to be prompted.
       if (gPrefService.getBoolPref("browser.privatebrowsing.dont_prompt_on_enter") ||
           gPrefService.getBoolPref("browser.privatebrowsing.keep_current_session"))
         return true;
     }
     catch (ex) { }
 
-    var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
-                        getService(Ci.nsIStringBundleService);
+    var bundleService = Services.strings;
     var pbBundle = bundleService.createBundle("chrome://browser/locale/browser.properties");
     var brandBundle = bundleService.createBundle("chrome://branding/locale/brand.properties");
 
     var appName = brandBundle.GetStringFromName("brandShortName");
 # On Mac, use the header as the title.
 #ifdef XP_MACOSX
     var dialogTitle = pbBundle.GetStringFromName("privateBrowsingMessageHeader");
     var header = "";
@@ -7327,19 +7334,17 @@ function safeModeRestart()
                     (Services.prompt.BUTTON_POS_1 *
                      Services.prompt.BUTTON_TITLE_CANCEL) +
                     Services.prompt.BUTTON_POS_0_DEFAULT;
 
   let rv = Services.prompt.confirmEx(window, promptTitle, promptMessage,
                                      buttonFlags, restartText, null, null,
                                      null, {});
   if (rv == 0) {
-    let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].
-                     getService(Ci.nsIAppStartup);
-    appStartup.restartInSafeMode(Ci.nsIAppStartup.eAttemptQuit);
+    Services.startup.restartInSafeMode(Ci.nsIAppStartup.eAttemptQuit);
   }
 }
 
 /* duplicateTabIn duplicates tab in a place specified by the parameter |where|.
  *
  * |where| can be:
  *  "tab"         new tab
  *  "tabshifted"  same as "tab" but in background if default is to select new
@@ -7396,19 +7401,17 @@ XPCOMUtils.defineLazyGetter(ResponsiveUI
   let tmp = {};
   Cu.import("resource:///modules/devtools/responsivedesign.jsm", tmp);
   return tmp.ResponsiveUIManager;
 });
 
 XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
 #ifdef XP_WIN
   // Only show resizers on Windows 2000 and XP
-  let sysInfo = Components.classes["@mozilla.org/system-info;1"]
-                          .getService(Components.interfaces.nsIPropertyBag2);
-  return parseFloat(sysInfo.getProperty("version")) < 6;
+  return parseFloat(Services.sysinfo.getProperty("version")) < 6;
 #else
   return false;
 #endif
 });
 
 var MousePosTracker = {
   _listeners: [],
   _x: 0,
@@ -7468,14 +7471,14 @@ var MousePosTracker = {
     } else {
       if (listener.onMouseLeave)
         listener.onMouseLeave();
     }
   }
 };
 
 function focusNextFrame(event) {
-  let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
+  let fm = Services.focus;
   let dir = event.shiftKey ? fm.MOVEFOCUS_BACKWARDDOC : fm.MOVEFOCUS_FORWARDDOC;
   let element = fm.moveFocus(window, null, dir, fm.FLAG_BYKEY);
   if (element.ownerDocument == document)
     focusAndSelectUrlBar();
 }
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -459,16 +459,23 @@
         <label value="&getUserMedia.selectMicrophone.label;"
                accesskey="&getUserMedia.selectMicrophone.accesskey;"
                control="webRTC-selectMicrophone-menulist"/>
         <menulist id="webRTC-selectMicrophone-menulist">
           <menupopup id="webRTC-selectMicrophone-menupopup"/>
         </menulist>
       </popupnotificationcontent>
     </popupnotification>
+
+    <popupnotification id="geolocation-notification" hidden="true">
+      <popupnotificationcontent orient="vertical" align="start">
+        <separator class="thin"/>
+        <label id="geolocation-learnmore-link" class="text-link"/>
+      </popupnotificationcontent>
+    </popupnotification>
   </popupset>
 
 #ifdef CAN_DRAW_IN_TITLEBAR
 <vbox id="titlebar">
   <hbox id="titlebar-content">
     <hbox id="appmenu-button-container">
       <button id="appmenu-button"
               type="menu"
@@ -516,17 +523,17 @@
       <hbox class="titlebar-placeholder" type="caption-buttons" ordinal="1000"/>
 #endif
     </toolbar>
 
     <toolbar id="nav-bar" class="toolbar-primary chromeclass-toolbar"
              toolbarname="&navbarCmd.label;" accesskey="&navbarCmd.accesskey;"
              fullscreentoolbar="true" mode="icons" customizable="true"
              iconsize="large"
-             defaultset="unified-back-forward-button,urlbar-container,reload-button,stop-button,search-container,downloads-button,home-button,bookmarks-menu-button-container,window-controls"
+             defaultset="unified-back-forward-button,urlbar-container,reload-button,stop-button,search-container,webrtc-status-button,downloads-button,home-button,bookmarks-menu-button-container,window-controls"
              context="toolbar-context-menu">
 
       <toolbaritem id="unified-back-forward-button" class="chromeclass-toolbar-additional"
                    context="backForwardMenu" removable="true"
                    forwarddisabled="true"
                    title="&backForwardItem.title;">
         <toolbarbutton id="back-button" class="toolbarbutton-1"
                        label="&backCmd.label;"
@@ -573,16 +580,18 @@
             <image id="identity-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="geo-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="addons-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="indexedDB-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="password-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="webapps-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="plugins-notification-icon" class="notification-anchor-icon" role="button"/>
             <image id="blocked-plugins-notification-icon" class="notification-anchor-icon" role="button"/>
+            <image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"/>
+            <image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"/>
           </box>
           <!-- Use onclick instead of normal popup= syntax since the popup
                code fires onmousedown, and hence eats our favicon drag events.
                We only add the identity-box button to the tab order when the location bar
                has focus, otherwise pressing F6 focuses it instead of the location bar -->
           <box id="identity-box" role="button"
                align="center"
                onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
@@ -648,16 +657,26 @@
                      tooltiptext="&stopButton.tooltip;"/>
 
       <toolbaritem id="search-container" title="&searchItem.title;"
                    align="center" class="chromeclass-toolbar-additional"
                    flex="100" persist="width" removable="true">
         <searchbar id="searchbar" flex="1"/>
       </toolbaritem>
 
+      <toolbarbutton id="webrtc-status-button"
+                     class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     type="menu"
+                     hidden="true"
+                     orient="horizontal">
+        <menupopup onpopupshowing="WebrtcIndicator.fillPopup(this);"
+                   onpopuphiding="WebrtcIndicator.clearPopup(this);"
+                   oncommand="WebrtcIndicator.menuCommand(event.target);"/>
+      </toolbarbutton>
+
       <toolbarbutton id="home-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      persist="class" removable="true"
                      label="&homeButton.label;"
                      ondragover="homeButtonObserver.onDragOver(event)"
                      ondragenter="homeButtonObserver.onDragOver(event)"
                      ondrop="homeButtonObserver.onDrop(event)"
                      ondragexit="homeButtonObserver.onDragExit(event)"
                      onclick="BrowserGoHome(event);"
@@ -1045,16 +1064,18 @@
       <toolbarbutton id="tabview-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      label="&tabGroupsButton.label;"
                      command="Browser:ToggleTabView"
                      tooltiptext="&tabGroupsButton.tooltip;"
                      observes="tabviewGroupsNumber"/>
     </toolbarpalette>
   </toolbox>
 
+  <hbox id="fullscr-toggler" collapsed="true"/>
+
   <hbox flex="1" id="browser">
     <vbox id="browser-border-start" hidden="true" layer="true"/>
     <vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
       <sidebarheader id="sidebar-header" align="center">
         <label id="sidebar-title" persist="value" flex="1" crop="end" control="sidebar"/>
         <image id="sidebar-throbber"/>
         <toolbarbutton class="tabs-closebutton" tooltiptext="&sidebarCloseButton.tooltip;" oncommand="toggleSidebar();"/>
       </sidebarheader>
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -846,21 +846,21 @@
               TelemetryStopwatch.start("FX_TAB_SWITCH_UPDATE_MS");
               window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils)
                                                              .beginTabSwitch();
             }
 
             var oldTab = this.mCurrentTab;
 
             // Preview mode should not reset the owner
-            if (!this._previewMode && oldTab != this.selectedTab)
+            if (!this._previewMode && !oldTab.selected)
               oldTab.owner = null;
 
             if (this._lastRelatedTab) {
-              if (this._lastRelatedTab != this.selectedTab)
+              if (!this._lastRelatedTab.selected)
                 this._lastRelatedTab.owner = null;
               this._lastRelatedTab = null;
             }
 
             var oldBrowser = this.mCurrentBrowser;
             if (oldBrowser) {
               oldBrowser.setAttribute("type", "content-targetable");
               oldBrowser.docShellIsActive = false;
@@ -871,17 +871,17 @@
                 (oldBrowser.pageReport && !newBrowser.pageReport) ||
                 (!oldBrowser.pageReport && newBrowser.pageReport))
               updatePageReport = true;
 
             newBrowser.setAttribute("type", "content-primary");
             newBrowser.docShellIsActive =
               (window.windowState != window.STATE_MINIMIZED);
             this.mCurrentBrowser = newBrowser;
-            this.mCurrentTab = this.selectedTab;
+            this.mCurrentTab = this.tabContainer.selectedItem;
             this.showTab(this.mCurrentTab);
 
             var backForwardContainer = document.getElementById("unified-back-forward-button");
             if (backForwardContainer) {
               backForwardContainer.setAttribute("switchingtabs", "true");
               window.addEventListener("MozAfterPaint", function removeSwitchingtabsAttr() {
                 window.removeEventListener("MozAfterPaint", removeSwitchingtabsAttr);
                 backForwardContainer.removeAttribute("switchingtabs");
@@ -952,18 +952,17 @@
               this.mIsBusy = false;
               this._callProgressListeners(null, "onStateChange",
                                           [webProgress, null,
                                            nsIWebProgressListener.STATE_STOP |
                                            nsIWebProgressListener.STATE_IS_NETWORK, 0],
                                           true, false);
             }
 
-            if (this.mCurrentTab.selected)
-              this._setCloseKeyState(!this.mCurrentTab.pinned);
+            this._setCloseKeyState(!this.mCurrentTab.pinned);
 
             // TabSelect events are suppressed during preview mode to avoid confusing extensions and other bits of code
             // that might rely upon the other changes suppressed.
             // Focus is suppressed in the event that the main browser window is minimized - focusing a tab would restore the window
             if (!this._previewMode) {
               // We've selected the new tab, so go ahead and notify listeners.
               let event = document.createEvent("Events");
               event.initEvent("TabSelect", true, false);
@@ -1666,17 +1665,17 @@
             var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
                               getInterface(Ci.nsIDOMWindowUtils);
             windowUtils.preventFurtherDialogs();
 
             // Remove the tab's filter and progress listener.
             const filter = this.mTabFilters[aTab._tPos];
 #ifdef MOZ_E10S_COMPAT
             // Bug 666801 - WebProgress support for e10s
-#else 
+#else
             browser.webProgress.removeProgressListener(filter);
 #endif
             filter.removeProgressListener(this.mTabListeners[aTab._tPos]);
             this.mTabListeners[aTab._tPos].destroy();
 
             if (browser.registeredOpenURI && !aTabWillBeMoved) {
               this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI);
               delete browser.registeredOpenURI;
@@ -1820,17 +1819,17 @@
           ]]>
         </body>
       </method>
 
       <method name="_blurTab">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
-            if (this.mCurrentTab != aTab)
+            if (!aTab.selected)
               return;
 
             if (aTab.owner &&
                 !aTab.owner.hidden &&
                 !aTab.owner.closing &&
                 Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")) {
               this.selectedTab = aTab.owner;
               return;
@@ -1870,26 +1869,26 @@
         <body>
           <![CDATA[
             // The browser must be standalone.
             if (aBrowser.getTabBrowser())
               throw Cr.NS_ERROR_INVALID_ARG;
 
             // The tab is definitely not loading.
             aNewTab.removeAttribute("busy");
-            if (aNewTab == this.selectedTab) {
+            if (aNewTab.selected) {
               this.mIsBusy = false;
             }
 
             this._swapBrowserDocShells(aNewTab, aBrowser);
 
             // Update the new tab's title.
             this.setTabTitle(aNewTab);
 
-            if (aNewTab == this.selectedTab) {
+            if (aNewTab.selected) {
               this.updateCurrentBrowser(true);
             }
           ]]>
         </body>
       </method>
 
       <method name="swapBrowsersAndCloseOther">
         <parameter name="aOurTab"/>
@@ -1918,33 +1917,33 @@
             let ourBrowser = this.getBrowserForTab(aOurTab);
             let otherBrowser = aOtherTab.linkedBrowser;
             if (!ourBrowser.mIconURL && otherBrowser.mIconURL)
               this.setIcon(aOurTab, otherBrowser.mIconURL);
             var isBusy = aOtherTab.hasAttribute("busy");
             if (isBusy) {
               aOurTab.setAttribute("busy", "true");
               this._tabAttrModified(aOurTab);
-              if (aOurTab == this.selectedTab)
+              if (aOurTab.selected)
                 this.mIsBusy = true;
             }
 
             this._swapBrowserDocShells(aOurTab, otherBrowser);
 
             // Finish tearing down the tab that's going away.
             remoteBrowser._endRemoveTab(aOtherTab);
 
             if (isBusy)
               this.setTabTitleLoading(aOurTab);
             else
               this.setTabTitle(aOurTab);
 
             // If the tab was already selected (this happpens in the scenario
             // of replaceTabWithWindow), notify onLocationChange, etc.
-            if (aOurTab == this.selectedTab)
+            if (aOurTab.selected)
               this.updateCurrentBrowser(true);
           ]]>
         </body>
       </method>
 
       <method name="_swapBrowserDocShells">
         <parameter name="aOurTab"/>
         <parameter name="aOtherBrowser"/>
@@ -2131,17 +2130,17 @@
             aEvent.stopPropagation();
           }
         ]]>
         </body>
       </method>
 
       <property name="selectedTab">
         <getter>
-          return this.mTabBox.selectedTab;
+          return this.mCurrentTab;
         </getter>
         <setter>
           <![CDATA[
           // Update the tab
           this.mTabBox.selectedTab = val;
           return val;
           ]]>
         </setter>
@@ -2198,93 +2197,96 @@
           if (oldPosition == aIndex)
             return;
 
           this._lastRelatedTab = null;
 
           this.mTabFilters.splice(aIndex, 0, this.mTabFilters.splice(aTab._tPos, 1)[0]);
           this.mTabListeners.splice(aIndex, 0, this.mTabListeners.splice(aTab._tPos, 1)[0]);
 
+          let wasFocused = (document.activeElement == this.mCurrentTab);
+
           aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
           this.mCurrentTab._selected = false;
 
           // invalidate caches
           this._browsers = null;
           this._visibleTabs = null;
 
           // use .item() instead of [] because dragging to the end of the strip goes out of
           // bounds: .item() returns null (so it acts like appendChild), but [] throws
           this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex));
 
           for (let i = 0; i < this.tabs.length; i++) {
             this.tabs[i]._tPos = i;
             this.tabs[i]._selected = false;
           }
           this.mCurrentTab._selected = true;
+
+          if (wasFocused)
+            this.mCurrentTab.focus();
+
           this.tabContainer._handleTabSelect(false);
 
           if (aTab.pinned)
             this.tabContainer._positionPinnedTabs();
 
           var evt = document.createEvent("UIEvents");
           evt.initUIEvent("TabMove", true, false, window, oldPosition);
           aTab.dispatchEvent(evt);
         ]]>
         </body>
       </method>
 
       <method name="moveTabForward">
         <body>
           <![CDATA[
-            var tabPos = this.mCurrentTab._tPos;
-            if (tabPos < this.browsers.length - 1) {
-              this.moveTabTo(this.mCurrentTab, tabPos + 1);
-              this.mCurrentTab.focus();
-            }
+            let nextTab = this.mCurrentTab.nextSibling;
+            while (nextTab && nextTab.hidden)
+              nextTab = nextTab.nextSibling;
+
+            if (nextTab)
+              this.moveTabTo(this.mCurrentTab, nextTab._tPos);
             else if (this.arrowKeysShouldWrap)
               this.moveTabToStart();
           ]]>
         </body>
       </method>
 
       <method name="moveTabBackward">
         <body>
           <![CDATA[
-            var tabPos = this.mCurrentTab._tPos;
-            if (tabPos > 0) {
-              this.moveTabTo(this.mCurrentTab, tabPos - 1);
-              this.mCurrentTab.focus();
-            }
+            let previousTab = this.mCurrentTab.previousSibling;
+            while (previousTab && previousTab.hidden)
+              previousTab = previousTab.previousSibling;
+
+            if (previousTab)
+              this.moveTabTo(this.mCurrentTab, previousTab._tPos);
             else if (this.arrowKeysShouldWrap)
               this.moveTabToEnd();
           ]]>
         </body>
       </method>
 
       <method name="moveTabToStart">
         <body>
           <![CDATA[
             var tabPos = this.mCurrentTab._tPos;
-            if (tabPos > 0) {
+            if (tabPos > 0)
               this.moveTabTo(this.mCurrentTab, 0);
-              this.mCurrentTab.focus();
-            }
           ]]>
         </body>
       </method>
 
       <method name="moveTabToEnd">
         <body>
           <![CDATA[
             var tabPos = this.mCurrentTab._tPos;
-            if (tabPos < this.browsers.length - 1) {
-              this.moveTabTo(this.mCurrentTab,
-                             this.browsers.length - 1);
-              this.mCurrentTab.focus();
-            }
+            if (tabPos < this.browsers.length - 1)
+              this.moveTabTo(this.mCurrentTab, this.browsers.length - 1);
           ]]>
         </body>
       </method>
 
       <method name="moveTabOver">
         <parameter name="aEvent"/>
         <body>
           <![CDATA[
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -287,16 +287,17 @@ endif
                  browser_bug820497.js \
                  blockPluginVulnerableUpdatable.xml \
                  blockPluginVulnerableNoUpdate.xml \
                  blockNoPlugins.xml \
                  browser_utilityOverlay.js \
                  browser_bug676619.js \
                  download_page.html \
                  browser_URLBarSetURI.js \
+                 browser_bookmark_titles.js \
                  $(NULL)
 
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 _BROWSER_FILES += \
 		browser_bug462289.js \
 		$(NULL)
 else
 _BROWSER_FILES += \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bookmark_titles.js
@@ -0,0 +1,75 @@
+/* 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 file is tests for the default titles that new bookmarks get.
+
+let tests = [
+    ['http://example.com/browser/browser/base/content/test/dummy_page.html',
+     'Dummy test page'],
+    ['data:text/html;charset=utf-8,<title>test data: url</title>',
+     'test data: url'],
+    ['http://unregistered-domain.example',
+     'http://unregistered-domain.example/'],
+    ['https://untrusted.example.com/somepage.html',
+     'https://untrusted.example.com/somepage.html']
+];
+
+function generatorTest() {
+    gBrowser.selectedTab = gBrowser.addTab();
+    let browser = gBrowser.selectedBrowser;
+
+    browser.addEventListener("DOMContentLoaded", nextStep, true);
+    registerCleanupFunction(function () {
+        browser.removeEventListener("DOMContentLoaded", nextStep, true);
+        gBrowser.removeCurrentTab();
+    });
+
+    yield; // Wait for the new tab to load.
+
+    // Test that a bookmark of each URI gets the corresponding default title.
+    for (let i = 0; i < tests.length; ++i) {
+        let [uri, title] = tests[i];
+        content.location = uri;
+        yield;
+        checkBookmark(uri, title);
+    }
+
+    // Network failure test: now that dummy_page.html is in history, bookmarking
+    // it should give the last known page title as the default bookmark title.
+
+    // Simulate a network outage with offline mode. (Localhost is still
+    // accessible in offline mode, so disable the test proxy as well.)
+    BrowserOffline.toggleOfflineStatus();
+    let proxy = Services.prefs.getIntPref('network.proxy.type');
+    Services.prefs.setIntPref('network.proxy.type', 0);
+    registerCleanupFunction(function () {
+        BrowserOffline.toggleOfflineStatus();
+        Services.prefs.setIntPref('network.proxy.type', proxy);
+    });
+
+    // LOAD_FLAGS_BYPASS_CACHE isn't good enough. So clear the cache.
+    Services.cache.evictEntries(Services.cache.STORE_ANYWHERE);
+
+    let [uri, title] = tests[0];
+    content.location = uri;
+    yield;
+    // The offline mode test is only good if the page failed to load.
+    is(content.document.documentURI.substring(0, 14), 'about:neterror',
+        "Offline mode successfully simulated network outage.");
+    checkBookmark(uri, title);
+}
+
+// Bookmark the current page and confirm that the new bookmark has the expected
+// title. (Then delete the bookmark.)
+function checkBookmark(uri, expected_title) {
+    PlacesCommandHook.bookmarkCurrentPage(false);
+    
+    let id = PlacesUtils.getMostRecentBookmarkForURI(PlacesUtils._uri(uri));
+    let title = PlacesUtils.bookmarks.getItemTitle(id);
+
+    is(title, expected_title, "Bookmark got a good default title.");
+
+    PlacesUtils.bookmarks.removeItem(id);
+}
+
--- a/browser/base/content/test/browser_bug623155.js
+++ b/browser/base/content/test/browser_bug623155.js
@@ -77,32 +77,32 @@ var gWebProgressListener = {
 
   onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) {
     if (!aRequest) {
       // This is bug 673752, or maybe initial "about:blank".
       return;
     }
 
     ok(gNewTab, "There is a new tab.");
-    ok(isRedirectedURI(aLocation), 
+    ok(isRedirectedURI(aLocation),
        "onLocationChange catches only redirected URI.");
 
     if (aLocation.ref == "BG") {
       // This is background tab's request.
       isnot(gNewTab, gBrowser.selectedTab, "This is a background tab.");
     } else if (aLocation.ref == "FG") {
       // This is foreground tab's request.
       is(gNewTab, gBrowser.selectedTab, "This is a foreground tab.");
     }
     else {
       // We shonuld not reach here.
       ok(false, "This URI hash is not expected:" + aLocation.ref);
     }
 
-    let isSelectedTab = (gNewTab == gBrowser.selectedTab);
+    let isSelectedTab = gNewTab.selected;
     setTimeout(delayed, 0, isSelectedTab);
   }
 };
 
 function delayed(aIsSelectedTab) {
   // Switch tab and confirm URL bar.
   if (!aIsSelectedTab) {
     gBrowser.selectedTab = gNewTab;
--- a/browser/base/content/test/browser_ctrlTab.js
+++ b/browser/base/content/test/browser_ctrlTab.js
@@ -54,17 +54,17 @@ function test() {
     pressCtrlTab();
     EventUtils.synthesizeKey("w", { ctrlKey: true });
     ok(!tabToRemove.parentNode,
        "Ctrl+Tab*2 -> Ctrl+W removes the second most recently selected tab");
 
     pressCtrlTab(true);
     pressCtrlTab(true);
     releaseCtrl();
-    ok(gBrowser.selectedTab == selectedTab,
+    ok(selectedTab.selected,
        "Ctrl+Tab*2 -> Ctrl+W -> Ctrl+Shift+Tab*2 keeps the selected tab");
   }
   gBrowser.removeTab(gBrowser.tabContainer.lastChild);
   checkTabs(2);
 
   ctrlTabTest([1], 1, 0);
 
   gBrowser.removeTab(gBrowser.tabContainer.lastChild);
--- a/browser/base/content/test/browser_popupNotification.js
+++ b/browser/base/content/test/browser_popupNotification.js
@@ -702,23 +702,22 @@ function checkPopup(popup, notificationO
     is(popup.anchorNode.className, "notification-anchor-icon", "notification anchored to icon");
   }
   is(notification.getAttribute("label"), notificationObj.message, "message matches");
   is(notification.id, notificationObj.id + "-notification", "id matches");
   if (notificationObj.mainAction) {
     is(notification.getAttribute("buttonlabel"), notificationObj.mainAction.label, "main action label matches");
     is(notification.getAttribute("buttonaccesskey"), notificationObj.mainAction.accessKey, "main action accesskey matches");
   }
-  let actualSecondaryActions = notification.childNodes;
+  let actualSecondaryActions = Array.filter(notification.childNodes,
+                                            function (child) child.nodeName == "menuitem");
   let secondaryActions = notificationObj.secondaryActions || [];
   let actualSecondaryActionsCount = actualSecondaryActions.length;
   if (secondaryActions.length) {
-    let lastChild = actualSecondaryActions.item(actualSecondaryActions.length - 1);
-    is(lastChild.tagName, "menuseparator", "menuseparator exists");
-    actualSecondaryActionsCount--;
+    is(notification.lastChild.tagName, "menuseparator", "menuseparator exists");
   }
   is(actualSecondaryActionsCount, secondaryActions.length, actualSecondaryActions.length + " secondary actions");
   secondaryActions.forEach(function (a, i) {
     is(actualSecondaryActions[i].getAttribute("label"), a.label, "label for secondary action " + i + " matches");
     is(actualSecondaryActions[i].getAttribute("accesskey"), a.accessKey, "accessKey for secondary action " + i + " matches");
   });
 }
 
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -898,60 +898,16 @@
             return label;
           ]]>
         </body>
       </method>
 
     </implementation>
   </binding>
 
-  <binding id="geolocation-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
-    <content align="start">
-      <xul:image class="popup-notification-icon"
-                 xbl:inherits="popupid,src=icon"/>
-      <xul:vbox flex="1">
-        <xul:description class="popup-notification-description"
-                         xbl:inherits="xbl:text=label"/>
-        <xul:spacer flex="1"/>
-        <xul:hbox class="popup-notification-button-container"
-                  pack="end" align="center">
-          <xul:label anonid="learnmore" class="text-link geolocation-text-link"/>
-          <xul:spacer flex="1"/>
-          <xul:button anonid="button"
-                      type="menu-button"
-                      class="popup-notification-menubutton"
-                      xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
-            <xul:menupopup anonid="menupopup"
-                           xbl:inherits="oncommand=menucommand">
-              <children/>
-              <xul:menuitem class="menuitem-iconic popup-notification-closeitem"
-                            label="&closeNotificationItem.label;"
-                            xbl:inherits="oncommand=closeitemcommand"/>
-            </xul:menupopup>
-          </xul:button>
-        </xul:hbox>
-      </xul:vbox>
-      <xul:vbox pack="start">
-        <xul:toolbarbutton anonid="closebutton"
-                           class="messageCloseButton popup-notification-closebutton tabbable"
-                           xbl:inherits="oncommand=closebuttoncommand"
-                           tooltiptext="&closeNotification.tooltip;"/>
-      </xul:vbox>
-    </content>
-    <implementation>  
-      <constructor><![CDATA[
-        let link = document.getAnonymousElementByAttribute(this, "anonid", "learnmore");
-        link.value = gNavigatorBundle.getString("geolocation.learnMore");
-        
-        let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
-        link.href = formatter.formatURLPref("browser.geolocation.warning.infoURL");
-      ]]></constructor>
-    </implementation>
-  </binding>
-
   <binding id="addon-progress-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
     <content align="start">
       <xul:image class="popup-notification-icon"
                  xbl:inherits="popupid,src=icon"/>
       <xul:vbox flex="1">
         <xul:description class="popup-notification-description addon-progress-description"
                          xbl:inherits="xbl:text=label"/>
         <xul:spacer flex="1"/>
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -75,16 +75,18 @@ static RedirEntry kRedirMap[] = {
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT },
   { "newtab", "chrome://browser/content/newtab/newTab.xul",
     nsIAboutModule::ALLOW_SCRIPT },
   { "permissions", "chrome://browser/content/preferences/aboutPermissions.xul",
     nsIAboutModule::ALLOW_SCRIPT },
   { "preferences", "chrome://browser/content/preferences/in-content/preferences.xul",
     nsIAboutModule::ALLOW_SCRIPT },
+  { "downloads", "chrome://browser/content/downloads/contentAreaDownloadsView.xul",
+    nsIAboutModule::ALLOW_SCRIPT },
 };
 static const int kRedirTotal = NS_ARRAY_LENGTH(kRedirMap);
 
 static nsAutoCString
 GetAboutModuleName(nsIURI *aURI)
 {
   nsAutoCString path;
   aURI->GetPath(path);
--- a/browser/components/build/Makefile.in
+++ b/browser/components/build/Makefile.in
@@ -25,32 +25,35 @@ CPPSRCS = nsModule.cpp \
 
 ifeq ($(OS_ARCH),WINNT)
 OS_LIBS	+= $(call EXPAND_LIBNAME,ole32 shell32 shlwapi)
 endif
 
 LOCAL_INCLUDES = \
 	-I$(srcdir)/../shell/src \
 	-I$(srcdir)/../feeds/src \
-	-I$(srcdir)/../privatebrowsing/src \
 	-I$(srcdir)/../about \
 	-I$(srcdir)/../dirprovider \
 	$(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
 OS_LIBS += $(call EXPAND_LIBNAME,version)
 endif
 
 SHARED_LIBRARY_LIBS = \
 	../feeds/src/$(LIB_PREFIX)browser_feeds_s.$(LIB_SUFFIX) \
-	../privatebrowsing/src/$(LIB_PREFIX)privatebrowsing_s.$(LIB_SUFFIX) \
 	../about/$(LIB_PREFIX)browserabout_s.$(LIB_SUFFIX) \
 	../dirprovider/$(LIB_PREFIX)browserdir_s.$(LIB_SUFFIX) \
 	$(NULL)
 
+ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+LOCAL_INCLUDES += -I$(srcdir)/../privatebrowsing/src
+SHARED_LIBRARY_LIBS += ../privatebrowsing/src/$(LIB_PREFIX)privatebrowsing_s.$(LIB_SUFFIX)
+endif
+
 ifneq (,$(filter windows cocoa gtk2, $(MOZ_WIDGET_TOOLKIT)))
 SHARED_LIBRARY_LIBS += ../shell/src/$(LIB_PREFIX)shellservice_s.$(LIB_SUFFIX)
 endif
 
 EXTRA_DSO_LDOPTS += \
 	$(call EXPAND_LIBNAME_PATH,unicharutil_external_s,$(LIBXUL_DIST)/lib) \
 	$(XPCOM_STATICRUNTIME_GLUE_LDOPTS) \
 	$(MOZ_COMPONENT_LIBS) \
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -20,17 +20,19 @@
 #include "nsIEHistoryEnumerator.h"
 #endif
 
 #include "rdf.h"
 #include "nsFeedSniffer.h"
 #include "AboutRedirector.h"
 #include "nsIAboutModule.h"
 
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
 #include "nsPrivateBrowsingServiceWrapper.h"
+#endif
 #include "nsNetCID.h"
 
 using namespace mozilla::browser;
 
 /////////////////////////////////////////////////////////////////////////////
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(DirectoryProvider)
 #if defined(XP_WIN)
@@ -42,48 +44,54 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGN
 #endif
 
 #if defined(XP_WIN)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsIEHistoryEnumerator)
 #endif
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFeedSniffer)
 
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrivateBrowsingServiceWrapper, Init)
+#endif
 
 NS_DEFINE_NAMED_CID(NS_BROWSERDIRECTORYPROVIDER_CID);
 #if defined(XP_WIN)
 NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
 #elif defined(MOZ_WIDGET_GTK2)
 NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_FEEDSNIFFER_CID);
 NS_DEFINE_NAMED_CID(NS_BROWSER_ABOUT_REDIRECTOR_CID);
 #if defined(XP_WIN)
 NS_DEFINE_NAMED_CID(NS_WINIEHISTORYENUMERATOR_CID);
 #elif defined(XP_MACOSX)
 NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
 #endif
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
 NS_DEFINE_NAMED_CID(NS_PRIVATE_BROWSING_SERVICE_WRAPPER_CID);
+#endif
 
 static const mozilla::Module::CIDEntry kBrowserCIDs[] = {
     { &kNS_BROWSERDIRECTORYPROVIDER_CID, false, NULL, DirectoryProviderConstructor },
 #if defined(XP_WIN)
     { &kNS_SHELLSERVICE_CID, false, NULL, nsWindowsShellServiceConstructor },
 #elif defined(MOZ_WIDGET_GTK2)
     { &kNS_SHELLSERVICE_CID, false, NULL, nsGNOMEShellServiceConstructor },
 #endif
     { &kNS_FEEDSNIFFER_CID, false, NULL, nsFeedSnifferConstructor },
     { &kNS_BROWSER_ABOUT_REDIRECTOR_CID, false, NULL, AboutRedirector::Create },
 #if defined(XP_WIN)
     { &kNS_WINIEHISTORYENUMERATOR_CID, false, NULL, nsIEHistoryEnumeratorConstructor },
 #elif defined(XP_MACOSX)
     { &kNS_SHELLSERVICE_CID, false, NULL, nsMacShellServiceConstructor },
 #endif
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
     { &kNS_PRIVATE_BROWSING_SERVICE_WRAPPER_CID, false, NULL, nsPrivateBrowsingServiceWrapperConstructor },
+#endif
     { NULL }
 };
 
 static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
     { NS_BROWSERDIRECTORYPROVIDER_CONTRACTID, &kNS_BROWSERDIRECTORYPROVIDER_CID },
 #if defined(XP_WIN)
     { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
 #elif defined(MOZ_WIDGET_GTK2)
@@ -103,22 +111,25 @@ static const mozilla::Module::ContractID
 #ifdef MOZ_SERVICES_SYNC
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-tabs", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-progress", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #endif
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "home", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "permissions", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
+    { NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #if defined(XP_WIN)
     { NS_IEHISTORYENUMERATOR_CONTRACTID, &kNS_WINIEHISTORYENUMERATOR_CID },
 #elif defined(XP_MACOSX)
     { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
 #endif
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
     { NS_PRIVATE_BROWSING_SERVICE_CONTRACTID, &kNS_PRIVATE_BROWSING_SERVICE_WRAPPER_CID },
+#endif
     { NULL }
 };
 
 static const mozilla::Module::CategoryEntry kBrowserCategories[] = {
     { XPCOM_DIRECTORY_PROVIDER_CATEGORY, "browser-directory-provider", NS_BROWSERDIRECTORYPROVIDER_CONTRACTID },
     { NS_CONTENT_SNIFFER_CATEGORY, "Feed Sniffer", NS_FEEDSNIFFER_CONTRACTID },
     { NULL }
 };
--- a/browser/components/downloads/content/allDownloadsViewOverlay.css
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.css
@@ -16,18 +16,19 @@ richlistitem.download {
 .download-state:not(:-moz-any([state="1"], /* Finished           */
                               [state="2"], /* Failed             */
                               [state="3"], /* Canceled           */
                               [state="6"], /* Blocked (parental) */
                               [state="8"], /* Blocked (dirty)    */
                               [state="9"]) /* Blocked (policy)   */)
                                            .downloadRemoveFromHistoryMenuItem,
 .download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
-                              [state="5"], /* Starting (queued)  */
                               [state="0"], /* Downloading        */
-                              [state="4"]) /* Paused             */)
+                              [state="1"], /* Finished           */
+                              [state="4"], /* Paused             */
+                              [state="5"]) /* Starting (queued)  */)
                                            .downloadShowMenuItem,
 
 .download-state[state="7"]                 .downloadCommandsSeparator
 
 {
   display: none;
 }
--- a/browser/components/downloads/content/allDownloadsViewOverlay.js
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.js
@@ -91,17 +91,17 @@ DownloadElementShell.prototype = {
 
   set dataItem(aValue) {
     if ((this._dataItem = aValue)) {
       this._wasDone = this._dataItem.done;
       this._wasInProgress = this._dataItem.inProgress;
     }
     else if (this._placesNode) {
       this._wasInProgress = false;
-      this._wasDone = this._state == nsIDM.DOWNLOAD_FINISHED;
+      this._wasDone = this.getDownloadState(true) == nsIDM.DOWNLOAD_FINISHED;
     }
 
     this._updateStatusUI();
     return aValue;
   },
 
   _placesNode: null,
   get placesNode() this._placesNode,
@@ -110,17 +110,17 @@ DownloadElementShell.prototype = {
       // Preserve the annotations map if this is the first loading and we got
       // cached values.
       if (this._placesNode || !this._annotations) {
         this._annotations = new Map();
       }
       this._placesNode = aNode;
       if (!this._dataItem && this._placesNode) {
         this._wasInProgress = false;
-        this._wasDone = this._state == nsIDM.DOWNLOAD_FINISHED;
+        this._wasDone = this.getDownloadState(true) == nsIDM.DOWNLOAD_FINISHED;
         this._updateStatusUI();
       }
     }
     return aNode;
   },
 
   // The download uri (as a string)
   get downloadURI() {
@@ -211,52 +211,63 @@ DownloadElementShell.prototype = {
       }
     }
     return this.__file;
   },
 
   // The target's file size in bytes. If there's no target file, or If we
   // cannot determine its size, 0 is returned.
   get _fileSize() {
-    if (!this._file || !this._file.exists())
-      return 0;
-    try {
-      return this._file.fileSize;
+    if (!("__fileSize" in this)) {
+      if (!this._file || !this._file.exists())
+        this.__fileSize = 0;
+      try {
+        this.__fileSize = this._file.fileSize;
+      }
+      catch(ex) {
+        Cu.reportError(ex);
+        this.__fileSize = 0;
+      }
     }
-    catch(ex) {
-      Cu.reportError(ex);
-      return 0;
-    }
+    return this.__fileSize;
   },
 
+  /**
+   * Get the state of the download
+   * @param [optional] aForceUpdate
+   *        Whether to force update the cached download state. Default: false.
+   */
   // The download state (see nsIDownloadManager).
-  get _state() {
-    if (this._dataItem)
-      return this._dataItem.state;
-
-    let state = -1;
-    try {
-      return this._getAnnotation(DOWNLOAD_STATE_ANNO);
-    }
-    catch (ex) {
-      // The state annotation didn't exist in past releases.
-      if (!this._file) {
-        state = nsIDM.DOWNLOAD_FAILED;
-      }
-      else if (this._file.exists()) {
-        state = this._fileSize > 0 ?
-          nsIDM.DOWNLOAD_FINISHED : nsIDM.DOWNLOAD_FAILED;
+  getDownloadState: function DES_getDownloadState(aForceUpdate = false) {
+    if (aForceUpdate || !("_state" in this)) {
+      if (this._dataItem) {
+        this._state = this._dataItem.state;
       }
       else {
-        // XXXmano I'm not sure if this right. We should probably show no
-        // status text at all in this case.
-        state = nsIDM.DOWNLOAD_CANCELED;
+        try {
+          this._state = this._getAnnotation(DOWNLOAD_STATE_ANNO);
+        }
+        catch (ex) {
+          // The state annotation didn't exist in past releases.
+          if (!this._file) {
+            this._state = nsIDM.DOWNLOAD_FAILED;
+          }
+          else if (this._file.exists()) {
+            this._state = this._fileSize > 0 ?
+              nsIDM.DOWNLOAD_FINISHED : nsIDM.DOWNLOAD_FAILED;
+          }
+          else {
+            // XXXmano I'm not sure if this right. We should probably show no
+            // status text at all in this case.
+            this._state = nsIDM.DOWNLOAD_CANCELED;
+          }
+        }
       }
     }
-    return state;
+    return this._state;
   },
 
   // The status text for the download
   get _statusText() {
     let s = DownloadsCommon.strings;
     if (this._dataItem && this._dataItem.inProgress) {
       if (this._dataItem.paused) {
         let transfer =
@@ -287,17 +298,17 @@ DownloadElementShell.prototype = {
         DownloadUtils.getURIHost(this._dataItem.referrer ||
                                  this._dataItem.uri);
 
       let end = new Date(this.dataItem.endTime);
       let [displayDate, fullDate] = DownloadUtils.getReadableDates(end);
       return s.statusSeparator(fullHost, fullDate);
     }
 
-    switch (this._state) {
+    switch (this.getDownloadState()) {
       case nsIDM.DOWNLOAD_FAILED:
         return s.stateFailed;
       case nsIDM.DOWNLOAD_CANCELED:
         return s.stateCanceled;
       case nsIDM.DOWNLOAD_BLOCKED_PARENTAL:
         return s.stateBlockedParentalControls;
       case nsIDM.DOWNLOAD_BLOCKED_POLICY:
         return s.stateBlockedPolicy;
@@ -326,17 +337,17 @@ DownloadElementShell.prototype = {
     }
     return null;
   },
 
   // Updates the download state attribute (and by that hide/unhide the
   // appropriate buttons and context menu items), the status text label,
   // and the progress meter.
   _updateDownloadStatusUI: function  DES__updateDownloadStatusUI() {
-    this._element.setAttribute("state", this._state);
+    this._element.setAttribute("state", this.getDownloadState(true));
     this._element.setAttribute("status", this._statusText);
 
     // For past-downloads, we're done. For session-downloads, we may also need
     // to update the progress-meter.
     if (!this._dataItem)
       return;
 
     // Copied from updateProgress in downloads.js.
@@ -424,17 +435,17 @@ DownloadElementShell.prototype = {
   },
 
   /* nsIController */
   isCommandEnabled: function DES_isCommandEnabled(aCommand) {
     switch (aCommand) {
       case "downloadsCmd_open": {
         return this._file.exists() &&
                ((this._dataItem && this._dataItem.openable) ||
-                (this._state == nsIDM.DOWNLOAD_FINISHED));
+                (this.getDownloadState() == nsIDM.DOWNLOAD_FINISHED));
       }
       case "downloadsCmd_show": {
         return this._getTargetFileOrPartFileIfExists() != null;
       }
       case "downloadsCmd_pauseResume":
         return this._dataItem && this._dataItem.inProgress && this._dataItem.resumable;
       case "downloadsCmd_retry":
         return ((this._dataItem && this._dataItem.canRetry) ||
@@ -542,17 +553,17 @@ DownloadElementShell.prototype = {
           return "downloadsCmd_show";
         case nsIDM.DOWNLOAD_BLOCKED_PARENTAL:
         case nsIDM.DOWNLOAD_DIRTY:
         case nsIDM.DOWNLOAD_BLOCKED_POLICY:
           return "downloadsCmd_openReferrer";
       }
       return "";
     }
-    let command = getDefaultCommandForState(this._state);
+    let command = getDefaultCommandForState(this.getDownloadState());
     if (this.isCommandEnabled(command))
       this.doCommand(command);
   }
 };
 
 /**
  * A Downloads Places View is a places view designed to show a places query
  * for history donwloads alongside the current "session"-downloads.
@@ -584,16 +595,17 @@ function DownloadsPlacesView(aRichListBo
 
   // Register as a downloads view. The places data will be initialized by
   // the places setter.
   let downloadsData = DownloadsCommon.getData(window.opener || window);
   downloadsData.addView(this);
 
   // Make sure to unregister the view if the window is closed.
   window.addEventListener("unload", function() {
+    this._richlistbox.controllers.removeController(this);
     downloadsData.removeView(this);
     this.result = null;
   }.bind(this), true);
 }
 
 DownloadsPlacesView.prototype = {
   get associatedElement() this._richlistbox,
 
@@ -754,16 +766,17 @@ DownloadsPlacesView.prototype = {
     }
   },
 
   _removeElement: function DPV__removeElement(aElement) {
     // If the element was selected exclusively, select its next
     // sibling first, if any.
     if (aElement.nextSibling &&
         this._richlistbox.selectedItems &&
+        this._richlistbox.selectedItems.length > 0 &&
         this._richlistbox.selectedItems[0] == aElement) {
       this._richlistbox.selectItem(aElement.nextSibling);
     }
     this._richlistbox.removeChild(aElement);
   },
 
   _removeHistoryDownloadFromView:
   function DPV__removeHistoryDownloadFromView(aPlacesNode) {
@@ -866,18 +879,18 @@ DownloadsPlacesView.prototype = {
 
     return val;
   },
 
   get selectedNodes() {
     let placesNodes = [];
     let selectedElements = this._richlistbox.selectedItems;
     for (let elt of selectedElements) {
-      if (elt.placesNode)
-        placesNodes.push(elt.placesNode);
+      if (elt._shell.placesNode)
+        placesNodes.push(elt._shell.placesNode);
     }
     return placesNodes;
   },
 
   get selectedNode() {
     let selectedNodes = this.selectedNodes;
     return selectedNodes.length == 1 ? selectedNodes[0] : null;
   },
@@ -1074,17 +1087,17 @@ DownloadsPlacesView.prototype = {
   onContextMenu: function DPV_onContextMenu(aEvent)
   {
     let element = this._richlistbox.selectedItem;
     if (!element || !element._shell)
       return false;
 
     // Set the state attribute so that only the appropriate items are displayed.
     let contextMenu = document.getElementById("downloadsContextMenu");
-    contextMenu.setAttribute("state", element._shell._state);
+    contextMenu.setAttribute("state", element._shell.getDownloadState());
     return true;
   },
 
   onKeyPress: function DPV_onKeyPress(aEvent) {
     let selectedElements = this._richlistbox.selectedItems;
     if (!selectedElements)
       return;
 
--- a/browser/components/downloads/content/contentAreaDownloadsView.js
+++ b/browser/components/downloads/content/contentAreaDownloadsView.js
@@ -1,10 +1,15 @@
 /* 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/. */
 
+Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+
 let ContentAreaDownloadsView = {
   init: function CADV_init() {
     let view = new DownloadsPlacesView(document.getElementById("downloadsRichListBox"));
-    view.place = "place:transition=7&sort=4";
+    // Do not display the Places downloads in private windows
+    if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
+      view.place = "place:transition=7&sort=4";
+    }
   }
 };
--- a/browser/components/downloads/content/contentAreaDownloadsView.xul
+++ b/browser/components/downloads/content/contentAreaDownloadsView.xul
@@ -1,21 +1,29 @@
 <?xml version="1.0"?>
 
 # 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/.
 
 <?xml-stylesheet href="chrome://global/skin/"?>
+<?xml-stylesheet href="chrome://browser/skin/downloads/contentAreaDownloadsView.css"?>
+
 <?xul-overlay href="chrome://browser/content/downloads/allDownloadsViewOverlay.xul"?>
 
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 
+<!DOCTYPE window [
+<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
+%downloadsDTD;
+]>
+
 <window id="contentAreaDownloadsView"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="&downloads.title;"
         onload="ContentAreaDownloadsView.init();">
 
   <script type="application/javascript"
           src="chrome://global/content/globalOverlay.js"/>
   <script type="application/javascript"
           src="chrome://browser/content/downloads/contentAreaDownloadsView.js"/>
 
   <commandset id="editMenuCommands"/>
--- a/browser/components/downloads/content/downloads.css
+++ b/browser/components/downloads/content/downloads.css
@@ -52,19 +52,20 @@ richlistitem[type="download"]:not([selec
                               [state="2"], /* Failed             */
                               [state="3"], /* Canceled           */
                               [state="6"], /* Blocked (parental) */
                               [state="8"], /* Blocked (dirty)    */
                               [state="9"]) /* Blocked (policy)   */)
                                            .downloadRemoveFromHistoryMenuItem,
 
 .download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
-                              [state="5"], /* Starting (queued)  */
                               [state="0"], /* Downloading        */
-                              [state="4"]) /* Paused             */)
+                              [state="1"], /* Finished           */
+                              [state="4"], /* Paused             */
+                              [state="5"]) /* Starting (queued)  */)
                                            .downloadShowMenuItem,
 
 .download-state[state="7"]                 .downloadCommandsSeparator
 
 {
   display: none;
 }
 
--- a/browser/components/downloads/src/DownloadsUI.js
+++ b/browser/components/downloads/src/DownloadsUI.js
@@ -27,16 +27,18 @@ Cu.import("resource://gre/modules/XPCOMU
 
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
                                   "resource:///modules/DownloadsCommon.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "gBrowserGlue",
                                    "@mozilla.org/browser/browserglue;1",
                                    "nsIBrowserGlue");
 XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
                                   "resource:///modules/RecentWindow.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+                                  "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 ////////////////////////////////////////////////////////////////////////////////
 //// DownloadsUI
 
 function DownloadsUI()
 {
   XPCOMUtils.defineLazyGetter(this, "_toolkitUI", function () {
     // Create Toolkit's nsIDownloadManagerUI implementation.
@@ -102,60 +104,48 @@ DownloadsUI.prototype = {
   getAttention: function DUI_getAttention()
   {
     if (DownloadsCommon.useToolkitUI) {
       this._toolkitUI.getAttention();
     }
   },
 
   /**
-   * Helper function that opens the right download manager UI. Either the
-   * new Downloads View in Places, or the toolkit download window if the
-   * Places Downloads View is not enabled.
+   * Helper function that opens the download manager UI.
    */
   _showDownloadManagerUI:
   function DUI_showDownloadManagerUI(aWindowContext, aID, aReason)
   {
-    // First, determine if the Places Downloads view is preffed on.
-    let usePlacesView = false;
-    try {
-      usePlacesView =
-        Services.prefs.getBoolPref("browser.library.useNewDownloadsView");
-    } catch(e) {}
-
-    if (!usePlacesView) {
-      // If we got here, then the browser.library.useNewDownloadsView pref
-      // either didn't exist or was false, so just show the toolkit downloads
-      // manager.
-      this._toolkitUI.show(aWindowContext, aID, aReason);
-      return;
+    // If we weren't given a window context, try to find a browser window
+    // to use as our parent - and if that doesn't work, error out and give up.
+    let parentWindow = aWindowContext;
+    if (!parentWindow) {
+      parentWindow = RecentWindow.getMostRecentBrowserWindow();
+      if (!parentWindow) {
+        Components.utils.reportError(
+          "Couldn't find a browser window to open the Places Downloads View " +
+          "from.");
+        return;
+      }
     }
 
-    let organizer = Services.wm.getMostRecentWindow("Places:Organizer");
-    if (!organizer) {
-      let parentWindow = aWindowContext;
-      // If we weren't given a window context, try to find a browser window
-      // to use as our parent - and if that doesn't work, error out and give
-      // up.
-      if (!parentWindow) {
-        parentWindow = RecentWindow.getMostRecentBrowserWindow();
-        if (!parentWindow) {
-          Components.utils
-                    .reportError("Couldn't find a browser window to open " +
-                                 "the Places Downloads View from.");
-          return;
-        }
+    // If window is private then show it in a tab.
+    if (PrivateBrowsingUtils.isWindowPrivate(parentWindow)) {
+      parentWindow.openUILinkIn("about:downloads", "tab");
+      return;
+    } else {
+      let organizer = Services.wm.getMostRecentWindow("Places:Organizer");
+      if (!organizer) {
+        parentWindow.openDialog("chrome://browser/content/places/places.xul",
+                                "", "chrome,toolbar=yes,dialog=no,resizable",
+                                "Downloads");
+      } else {
+        organizer.PlacesOrganizer.selectLeftPaneQuery("Downloads");
+        organizer.focus();
       }
-      parentWindow.openDialog("chrome://browser/content/places/places.xul",
-                              "", "chrome,toolbar=yes,dialog=no,resizable",
-                              "Downloads");
-    }
-    else {
-      organizer.PlacesOrganizer.selectLeftPaneQuery("Downloads");
-      organizer.focus();
     }
   }
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Module
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DownloadsUI]);
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -546,29 +546,31 @@ BrowserGlue.prototype = {
       return;
 
     // There are several cases where we won't show a dialog here:
     // 1. There is only 1 tab open in 1 window
     // 2. The session will be restored at startup, indicated by
     //    browser.startup.page == 3 or browser.sessionstore.resume_session_once == true
     // 3. browser.warnOnQuit == false
     // 4. The browser is currently in Private Browsing mode
+    // 5. The browser will be restarted.
     //
     // Otherwise these are the conditions and the associated dialogs that will be shown:
     // 1. aQuitType == "lastwindow" or "quit" and browser.showQuitWarning == true
     //    - The quit dialog will be shown
-    // 2. aQuitType == "restart" && browser.warnOnRestart == true
-    //    - The restart dialog will be shown
-    // 3. aQuitType == "lastwindow" && browser.tabs.warnOnClose == true
+    // 2. aQuitType == "lastwindow" && browser.tabs.warnOnClose == true
     //    - The "closing multiple tabs" dialog will be shown
     //
     // aQuitType == "lastwindow" is overloaded. "lastwindow" is used to indicate
     // "the last window is closing but we're not quitting (a non-browser window is open)"
     // and also "we're quitting by closing the last window".
 
+    if (aQuitType == "restart")
+      return;
+
     var windowcount = 0;
     var pagecount = 0;
     var browserEnum = Services.wm.getEnumerator("navigator:browser");
     let allWindowsPrivate = true;
     while (browserEnum.hasMoreElements()) {
       windowcount++;
 
       var browser = browserEnum.getNext();
@@ -581,91 +583,73 @@ BrowserGlue.prototype = {
 
     this._saveSession = false;
     if (pagecount < 2)
       return;
 
     if (!aQuitType)
       aQuitType = "quit";
 
-    var showPrompt = false;
     var mostRecentBrowserWindow;
 
     // browser.warnOnQuit is a hidden global boolean to override all quit prompts
     // browser.showQuitWarning specifically covers quitting
-    // browser.warnOnRestart specifically covers app-initiated restarts where we restart the app
     // browser.tabs.warnOnClose is the global "warn when closing multiple tabs" pref
 
     var sessionWillBeRestored = Services.prefs.getIntPref("browser.startup.page") == 3 ||
                                 Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
     if (sessionWillBeRestored || !Services.prefs.getBoolPref("browser.warnOnQuit"))
       return;
 
     // On last window close or quit && showQuitWarning, we want to show the
     // quit warning.
-    if (aQuitType != "restart" && Services.prefs.getBoolPref("browser.showQuitWarning")) {
-      showPrompt = true;
-    }
-    else if (aQuitType == "restart" && Services.prefs.getBoolPref("browser.warnOnRestart")) {
-      showPrompt = true;
-    }
-    else if (aQuitType == "lastwindow") {
-      // If aQuitType is "lastwindow" and we aren't showing the quit warning,
-      // we should show the window closing warning instead. warnAboutClosing
-      // tabs checks browser.tabs.warnOnClose and returns if it's ok to close
-      // the window. It doesn't actually close the window.
-      mostRecentBrowserWindow = Services.wm.getMostRecentWindow("navigator:browser");
-      aCancelQuit.data = !mostRecentBrowserWindow.gBrowser.warnAboutClosingTabs(true);
+    if (!Services.prefs.getBoolPref("browser.showQuitWarning")) {
+      if (aQuitType == "lastwindow") {
+        // If aQuitType is "lastwindow" and we aren't showing the quit warning,
+        // we should show the window closing warning instead. warnAboutClosing
+        // tabs checks browser.tabs.warnOnClose and returns if it's ok to close
+        // the window. It doesn't actually close the window.
+        mostRecentBrowserWindow = Services.wm.getMostRecentWindow("navigator:browser");
+        aCancelQuit.data = !mostRecentBrowserWindow.gBrowser.warnAboutClosingTabs(true);
+      }
       return;
     }
 
     // Never show a prompt inside private browsing mode
     if (allWindowsPrivate)
       return;
 
-    if (!showPrompt)
-      return;
-
     var quitBundle = Services.strings.createBundle("chrome://browser/locale/quitDialog.properties");
     var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
 
     var appName = brandBundle.GetStringFromName("brandShortName");
-    var quitTitleString = (aQuitType == "restart" ? "restart" : "quit") + "DialogTitle";
+    var quitTitleString = "quitDialogTitle";
     var quitDialogTitle = quitBundle.formatStringFromName(quitTitleString, [appName], 1);
 
     var message;
-    if (aQuitType == "restart")
-      message = quitBundle.formatStringFromName("messageRestart",
-                                                [appName], 1);
-    else if (windowcount == 1)
+    if (windowcount == 1)
       message = quitBundle.formatStringFromName("messageNoWindows",
                                                 [appName], 1);
     else
       message = quitBundle.formatStringFromName("message",
                                                 [appName], 1);
 
     var promptService = Services.prompt;
 
     var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 +
                 promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 +
+                promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2 +
                 promptService.BUTTON_POS_0_DEFAULT;
 
     var neverAsk = {value:false};
-    var button0Title, button2Title;
+    var button0Title = quitBundle.GetStringFromName("saveTitle");
     var button1Title = quitBundle.GetStringFromName("cancelTitle");
+    var button2Title = quitBundle.GetStringFromName("quitTitle");
     var neverAskText = quitBundle.GetStringFromName("neverAsk");
 
-    if (aQuitType == "restart")
-      button0Title = quitBundle.GetStringFromName("restartTitle");
-    else {
-      flags += promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2;
-      button0Title = quitBundle.GetStringFromName("saveTitle");
-      button2Title = quitBundle.GetStringFromName("quitTitle");
-    }
-
     // This wouldn't have been set above since we shouldn't be here for
     // aQuitType == "lastwindow"
     mostRecentBrowserWindow = Services.wm.getMostRecentWindow("navigator:browser");
 
     var buttonChoice =
       promptService.confirmEx(mostRecentBrowserWindow, quitDialogTitle, message,
                               flags, button0Title, button1Title, button2Title,
                               neverAskText, neverAsk);
@@ -677,22 +661,18 @@ BrowserGlue.prototype = {
       break;
     case 1: // Cancel
       aCancelQuit.QueryInterface(Ci.nsISupportsPRBool);
       aCancelQuit.data = true;
       break;
     case 0: // Save & Quit
       this._saveSession = true;
       if (neverAsk.value) {
-        if (aQuitType == "restart")
-          Services.prefs.setBoolPref("browser.warnOnRestart", false);
-        else {
-          // always save state when shutting down
-          Services.prefs.setIntPref("browser.startup.page", 3);
-        }
+        // always save state when shutting down
+        Services.prefs.setIntPref("browser.startup.page", 3);
       }
       break;
     }
   },
 
   /*
    * _shouldShowRights - Determines if the user should be shown the
    * about:rights notification. The notification should *not* be shown if
@@ -925,32 +905,33 @@ BrowserGlue.prototype = {
     /*
      * Display an opt-out notification when telemetry is enabled by default,
      * an opt-in prompt otherwise.
      *
      * But do not display this prompt/notification if:
      *
      * - The last accepted/refused policy (either by accepting the prompt or by
      *   manually flipping the telemetry preference) is already at version
-     *   TELEMETRY_DISPLAY_REV.
+     *   TELEMETRY_DISPLAY_REV or higher (to avoid the prompt in tests).
      */
     var telemetryDisplayed;
     try {
       telemetryDisplayed = Services.prefs.getIntPref(PREF_TELEMETRY_DISPLAYED);
     } catch(e) {}
-    if (telemetryDisplayed === TELEMETRY_DISPLAY_REV)
+    if (telemetryDisplayed >= TELEMETRY_DISPLAY_REV)
       return;
 
 #ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
     /*
      * Additionally, in opt-out builds, don't display the notification if:
      *
      * - Telemetry is disabled
      * - Telemetry was explicitly refused through the UI
-     * - Opt-in telemetry was enabled and this is the first run with opt-out.
+     * - Opt-in telemetry was already enabled, don't notify the user until next
+     *   policy update. (Do the check only at first run with opt-out builds)
      */
 
     var telemetryEnabled = Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED);
     if (!telemetryEnabled)
       return;
 
     // If telemetry was explicitly refused through the UI,
     // also disable opt-out telemetry and bail out.
@@ -1786,16 +1767,20 @@ ContentPermissionPrompt.prototype = {
             Services.perms.addFromPrincipal(requestingPrincipal, "geo", Ci.nsIPermissionManager.DENY_ACTION);
             secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_NEVER_SHARE);
             request.cancel();
           }
         });
       }
     }
 
+    var link = chromeWin.document.getElementById("geolocation-learnmore-link");
+    link.value = browserBundle.GetStringFromName("geolocation.learnMore");
+    link.href = Services.urlFormatter.formatURLPref("browser.geolocation.warning.infoURL");
+
     var browser = chromeWin.gBrowser.getBrowserForDocument(requestingWindow.document);
 
     secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST);
     chromeWin.PopupNotifications.show(browser, "geolocation", message, "geo-notification-icon",
                                       mainAction, secondaryActions);
   }
 };
 
new file mode 100644
--- /dev/null
+++ b/browser/components/places/content/downloadsViewOverlay.xul
@@ -0,0 +1,29 @@
+<!-- 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/. -->
+
+<?xul-overlay href="chrome://browser/content/downloads/allDownloadsViewOverlay.xul"?>
+
+<overlay id="downloadsViewOverlay"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <script type="application/javascript"><![CDATA[
+    const DOWNLOADS_QUERY = "place:transition=" +
+      Components.interfaces.nsINavHistoryService.TRANSITION_DOWNLOAD +
+      "&sort=" +
+      Components.interfaces.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING;
+
+    ContentArea.setContentViewForQueryString(DOWNLOADS_QUERY,
+      function() new DownloadsPlacesView(document.getElementById("downloadsRichListBox")),
+      { showDetailsPane: false });
+  ]]></script>
+
+  <window id="places">
+    <commandset id="downloadCommands"/>
+    <menupopup id="downloadsContextMenu"/>
+  </window>
+
+  <deck id="placesViewsDeck">
+    <richlistbox id="downloadsRichListBox"/>
+  </deck>
+</overlay>
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -1,20 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 Components.utils.import("resource:///modules/MigrationUtils.jsm");
 
-const DOWNLOADS_QUERY = "place:transition=" +
-  Components.interfaces.nsINavHistoryService.TRANSITION_DOWNLOAD +
-  "&sort=" +
-  Components.interfaces.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING;
-
 var PlacesOrganizer = {
   _places: null,
 
   // IDs of fields from editBookmarkOverlay that should be hidden when infoBox
   // is minimal. IDs should be kept in sync with the IDs of the elements
   // observing additionalInfoBroadcaster.
   _additionalInfoFields: [
     "editBMPanel_descriptionRow",
@@ -82,17 +77,17 @@ var PlacesOrganizer = {
 
 #ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
     gPrivateBrowsingListener.init();
 #endif
 
     // Select the first item in the content area view.
     let view = ContentArea.currentView;
     let root = view.result ? view.result.root : null;
-    if (root && root.containerOpen && root.childCount >= 0)
+    if (root && root.containerOpen && root.childCount > 0)
       view.selectNode(root.getChild(0));
     ContentArea.focus();
   },
 
   QueryInterface: function PO_QueryInterface(aIID) {
     if (aIID.equals(Components.interfaces.nsIDOMEventListener) ||
         aIID.equals(Components.interfaces.nsISupports))
       return this;
@@ -274,16 +269,23 @@ var PlacesOrganizer = {
       }
     }
   },
 
   /**
    * Handle focus changes on the places list and the current content view.
    */
   updateDetailsPane: function PO_updateDetailsPane() {
+    let detailsDeck = document.getElementById("detailsDeck");
+    let detailsPaneDisabled = detailsDeck.hidden =
+      !ContentArea.currentViewOptions.showDetailsPane;
+    if (detailsPaneDisabled) {
+      return;
+    }
+
     let view = PlacesUIUtils.getViewForNode(document.activeElement);
     if (view) {
       let selectedNodes = view.selectedNode ?
                           [view.selectedNode] : view.selectedNodes;
       this._fillDetailsPane(selectedNodes);
     }
   },
 
@@ -1241,73 +1243,117 @@ let gPrivateBrowsingListener = {
       this._cmd_import.setAttribute("disabled", "true");
     else
       this._cmd_import.removeAttribute("disabled");
   }
 };
 #endif
 
 let ContentArea = {
+  _specialViews: new Map(),
+
   init: function CA_init() {
     this._deck = document.getElementById("placesViewsDeck");
-    this._specialViews = new Map();
     ContentTree.init();
   },
 
-  _shouldUseNewDownloadsView: function CA_shouldUseNewDownloadsView() {
-    try {
-      return Services.prefs.getBoolPref("browser.library.useNewDownloadsView");
-    }
-    catch(ex) { }
-    return false;
-  },
-
+  /**
+   * Gets the content view to be used for loading the given query.
+   * If a custom view was set by setContentViewForQueryString, that
+   * view would be returned, else the default tree view is returned
+   *
+   * @param aQueryString
+   *        a query string
+   * @return the view to be used for loading aQueryString.
+   */
   getContentViewForQueryString:
   function CA_getContentViewForQueryString(aQueryString) {
-    if (this._specialViews.has(aQueryString))
-      return this._specialViews.get(aQueryString);
-    if (aQueryString == DOWNLOADS_QUERY && this._shouldUseNewDownloadsView()) {
-      let view = new DownloadsPlacesView(document.getElementById("downloadsRichListBox"));
-      this.setContentViewForQueryString(aQueryString, view);
-      return view;
+    try {
+      if (this._specialViews.has(aQueryString)) {
+        let { view, options } = this._specialViews.get(aQueryString);
+        if (typeof view == "function") {
+          view = view();
+          this._specialViews.set(aQueryString, { view: view, options: options });
+        }
+        return view;
+      }
+    }
+    catch(ex) {
+      Cu.reportError(ex);
     }
     return ContentTree.view;
   },
 
+  /**
+   * Sets a custom view to be used rather than the default places tree
+   * whenever the given query is selected in the left pane.
+   * @param aQueryString
+   *        a query string
+   * @param aView
+   *        Either the custom view or a function that will return the view
+   *        the first (and only) time it's called.
+   * @param [optional] aOptions
+   *        Object defining special options for the view.
+   * @see ContentTree.viewOptions for supported options and default values.
+   */
   setContentViewForQueryString:
-  function CA_setContentViewForQueryString(aQueryString, aView) {
-    this._specialViews.set(aQueryString, aView);
+  function CA_setContentViewForQueryString(aQueryString, aView, aOptions) {
+    if (!aQueryString ||
+        typeof aView != "object" && typeof aView != "function")
+      throw new Error("Invalid arguments");
+
+    this._specialViews.set(aQueryString, { view: aView,
+                                           options: aOptions || new Object() });
   },
 
   get currentView() PlacesUIUtils.getViewForNode(this._deck.selectedPanel),
   set currentView(aView) {
     if (this.currentView != aView)
       this._deck.selectedPanel = aView.associatedElement;
     return aView;
   },
 
   get currentPlace() this.currentView.place,
   set currentPlace(aQueryString) {
     this.currentView = this.getContentViewForQueryString(aQueryString);
     this.currentView.place = aQueryString;
     return aQueryString;
   },
 
+  /**
+   * Options for the current view.
+   *
+   * @see ContentTree.viewOptions for supported options and default values.
+   */
+  get currentViewOptions() {
+    // Use ContentTree options as default.
+    let viewOptions = ContentTree.viewOptions;
+    if (this._specialViews.has(this.currentPlace)) {
+      let { view, options } = this._specialViews.get(this.currentPlace);
+      for (let option in options) {
+        viewOptions[option] = options[option];
+      }
+    }
+    return viewOptions;
+  },
+
   focus: function() {
     this._deck.selectedPanel.focus();
   }
 };
 
 let ContentTree = {
   init: function CT_init() {
     this._view = document.getElementById("placeContent");
   },
 
   get view() this._view,
 
+  get viewOptions() Object.seal({ showDetailsPane: true }),
+
   openSelectedNode: function CT_openSelectedNode(aEvent) {
     let view = this.view;
     PlacesUIUtils.openNodeWithEvent(view.selectedNode, aEvent, view);
   },
 
   onClick: function CT_onClick(aEvent) {
     // Only handle clicks on tree children.
     if (aEvent.target.localName != "treechildren")
--- a/browser/components/places/content/places.xul
+++ b/browser/components/places/content/places.xul
@@ -7,17 +7,16 @@
 <?xml-stylesheet href="chrome://browser/content/places/places.css"?>
 <?xml-stylesheet href="chrome://browser/content/places/organizer.css"?>
 
 <?xml-stylesheet href="chrome://global/skin/"?>
 <?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
 <?xml-stylesheet href="chrome://browser/skin/places/organizer.css"?>
 
 <?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?>
-<?xul-overlay href="chrome://browser/content/downloads/allDownloadsViewOverlay.xul"?>
 
 #ifdef XP_MACOSX
 <?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?>
 #else
 <?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?>
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
 #endif
@@ -400,17 +399,16 @@
             <treecol label="&col.dateadded.label;" id="placesContentDateAdded" anonid="dateAdded" flex="1" hidden="true"
                       persist="width hidden ordinal sortActive sortDirection"/>
             <splitter class="tree-splitter"/>
             <treecol label="&col.lastmodified.label;" id="placesContentLastModified" anonid="lastModified" flex="1" hidden="true"
                       persist="width hidden ordinal sortActive sortDirection"/>
           </treecols>
           <treechildren flex="1"/>
         </tree>
-        <richlistbox id="downloadsRichListBox"/>
       </deck>
       <deck id="detailsDeck" style="height: 11em;">
         <vbox id="itemsCountBox" align="center">
           <spacer flex="3"/>
           <label id="itemsCountText"/>
           <spacer flex="1"/>
           <description id="selectItemDescription">
               &detailsPane.selectAnItemText.description;
@@ -435,12 +433,9 @@
                     accesskey="&detailsPane.more.accesskey;"
                     control="infoBoxExpander"/>
 
           </hbox>
         </vbox>
       </deck>
     </vbox>
   </hbox>
-
-  <commandset id="downloadCommands"/>
-  <menupopup id="downloadsContextMenu"/>
 </window>
--- a/browser/components/places/jar.mn
+++ b/browser/components/places/jar.mn
@@ -1,13 +1,14 @@
 # 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/.
 
 browser.jar:
+% overlay chrome://browser/content/places/places.xul chrome://browser/content/places/downloadsViewOverlay.xul
 # Provide another URI for the bookmarkProperties dialog so we can persist the
 # attributes separately
     content/browser/places/bookmarkProperties2.xul       (content/bookmarkProperties.xul)
 *   content/browser/places/places.xul                    (content/places.xul) 
 *   content/browser/places/places.js                     (content/places.js)
     content/browser/places/places.css                    (content/places.css)
     content/browser/places/organizer.css                 (content/organizer.css)
     content/browser/places/bookmarkProperties.xul        (content/bookmarkProperties.xul)
@@ -25,8 +26,9 @@ browser.jar:
 # ditto for the bookmarks sidebar
     content/browser/bookmarks/bookmarksPanel.xul         (content/bookmarksPanel.xul)
     content/browser/bookmarks/bookmarksPanel.js          (content/bookmarksPanel.js)
 *   content/browser/bookmarks/sidebarUtils.js            (content/sidebarUtils.js)
     content/browser/places/moveBookmarks.xul             (content/moveBookmarks.xul)
     content/browser/places/moveBookmarks.js              (content/moveBookmarks.js)
     content/browser/places/editBookmarkOverlay.xul       (content/editBookmarkOverlay.xul)
     content/browser/places/editBookmarkOverlay.js        (content/editBookmarkOverlay.js)
+    content/browser/places/downloadsViewOverlay.xul      (content/downloadsViewOverlay.xul)
--- a/browser/components/places/tests/browser/browser_library_downloads.js
+++ b/browser/components/places/tests/browser/browser_library_downloads.js
@@ -10,47 +10,61 @@
  * are shown in it.
  */
 
 let now = Date.now();
 
 function test() {
   waitForExplicitFinish();
 
-  function onLibraryReady(win) {
+  let onLibraryReady = function(win) {
     // Add visits to compare contents with.
-    fastAddVisit("http://mozilla.com",
-                  PlacesUtils.history.TRANSITION_TYPED);
-    fastAddVisit("http://google.com",
-                  PlacesUtils.history.TRANSITION_DOWNLOAD);
-    fastAddVisit("http://en.wikipedia.org",
-                  PlacesUtils.history.TRANSITION_TYPED);
-    fastAddVisit("http://ubuntu.org",
-                  PlacesUtils.history.TRANSITION_DOWNLOAD);
-
-    // Make sure Downloads is present.
-    isnot(win.PlacesOrganizer._places.selectedNode, null,
-          "Downloads is present and selected");
+    let places = [
+      { uri: NetUtil.newURI("http://mozilla.com"),
+        visits: [ new VisitInfo(PlacesUtils.history.TRANSITION_TYPED) ]
+      },
+      { uri: NetUtil.newURI("http://google.com"),
+        visits: [ new VisitInfo(PlacesUtils.history.TRANSITION_DOWNLOAD) ]
+      },
+      { uri: NetUtil.newURI("http://en.wikipedia.org"),
+        visits: [ new VisitInfo(PlacesUtils.history.TRANSITION_TYPED) ]
+      },
+      { uri: NetUtil.newURI("http://ubuntu.org"),
+        visits: [ new VisitInfo(PlacesUtils.history.TRANSITION_DOWNLOAD) ]
+      },
+    ]
+    PlacesUtils.asyncHistory.updatePlaces(places, {
+      handleResult: function () {},
+      handleError: function () {
+        ok(false, "gHistory.updatePlaces() failed");
+      },
+      handleCompletion: function () {
+        // Make sure Downloads is present.
+        isnot(win.PlacesOrganizer._places.selectedNode, null,
+              "Downloads is present and selected");
 
-    // Make sure content in right pane exists.
-    let tree = win.document.getElementById("placeContent");
-    isnot(tree, null, "placeContent tree exists");
 
-    // Check results.
-    var contentRoot = tree.result.root;
-    var len = contentRoot.childCount;
-    var testUris = ["http://ubuntu.org/", "http://google.com/"];
-    for (var i = 0; i < len; i++) {
-      is(contentRoot.getChild(i).uri, testUris[i],
-          "Comparing downloads shown at index " + i);
-    }
+        // Check results.
+        let contentRoot = win.ContentArea.currentView.result.root;
+        let len = contentRoot.childCount;
+        const TEST_URIS = ["http://ubuntu.org/", "http://google.com/"];
+        for (let i = 0; i < len; i++) {
+          is(contentRoot.getChild(i).uri, TEST_URIS[i],
+              "Comparing downloads shown at index " + i);
+        }
 
-    win.close();
-    waitForClearHistory(finish);
+        win.close();
+        waitForClearHistory(finish);
+      }
+    })
   }
 
   openLibrary(onLibraryReady, "Downloads");
 }
 
-function fastAddVisit(uri, transition) {
-  PlacesUtils.history.addVisit(PlacesUtils._uri(uri), now++ * 1000,
-                               null, transition, false, 0);
+function VisitInfo(aTransitionType)
+{
+  this.transitionType =
+    aTransitionType === undefined ?
+      PlacesUtils.history.TRANSITION_LINK : aTransitionType;
+  this.visitDate = now++ * 1000;
 }
+VisitInfo.prototype = {}
--- a/browser/components/preferences/in-content/jar.mn
+++ b/browser/components/preferences/in-content/jar.mn
@@ -6,17 +6,17 @@ browser.jar:
 *  content/browser/preferences/in-content/preferences.js
    content/browser/preferences/in-content/landing.xul
 *  content/browser/preferences/in-content/preferences.xul
 *  content/browser/preferences/in-content/main.xul
    content/browser/preferences/in-content/main.js
 *  content/browser/preferences/in-content/tabs.xul
 *  content/browser/preferences/in-content/tabs.js
    content/browser/preferences/in-content/privacy.xul
-   content/browser/preferences/in-content/privacy.js
+*  content/browser/preferences/in-content/privacy.js
 *  content/browser/preferences/in-content/advanced.xul
 *  content/browser/preferences/in-content/advanced.js
    content/browser/preferences/in-content/applications.xul
 *  content/browser/preferences/in-content/applications.js
    content/browser/preferences/in-content/content.xul
    content/browser/preferences/in-content/content.js
    content/browser/preferences/in-content/sync.xul
    content/browser/preferences/in-content/sync.js
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -1,34 +1,50 @@
 /* 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/. */
 
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
+                                  "resource:///modules/DownloadsCommon.jsm");
+
 var gMainPane = {
   _pane: null,
 
   /**
    * Initialization of this.
    */
   init: function ()
   {
     this._pane = document.getElementById("paneMain");
 
     // set up the "use current page" label-changing listener
     this._updateUseCurrentButton();
     window.addEventListener("focus", this._updateUseCurrentButton.bind(this), false);
 
     this.updateBrowserStartupLastSession();
 
+    this.setupDownloadsWindowOptions();
+
     // Notify observers that the UI is now ready
     Components.classes["@mozilla.org/observer-service;1"]
               .getService(Components.interfaces.nsIObserverService)
               .notifyObservers(window, "main-pane-loaded", null);
   },
 
+  setupDownloadsWindowOptions: function ()
+  {
+    let showWhenDownloading = document.getElementById("showWhenDownloading");
+    let closeWhenDone = document.getElementById("closeWhenDone");
+
+    // These radio buttons should be hidden when the Downloads Panel is enabled.
+    let shouldHide = !DownloadsCommon.useToolkitUI;
+    showWhenDownloading.hidden = shouldHide;
+    closeWhenDone.hidden = shouldHide;
+  },
+
   // HOME PAGE
 
   /*
    * Preferences:
    *
    * browser.startup.homepage
    * - the user's home page, as a string; if the home page is a set of tabs,
    *   this will be those URLs separated by the pipe character "|"
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -5,16 +5,21 @@
 var gPrivacyPane = {
 
   /**
    * Whether the use has selected the auto-start private browsing mode in the UI.
    */
   _autoStartPrivateBrowsing: false,
 
   /**
+   * Whether the prompt to restart Firefox should appear when changing the autostart pref.
+   */
+  _shouldPromptForRestart: true,
+
+  /**
    * Sets up the UI for the number of days of history to keep, and updates the
    * label of the "Clear Now..." button.
    */
   init: function ()
   {
     this._updateSanitizeSettingsButton();
     this.initializeHistoryMode();
     this.updateHistoryModePane();
@@ -122,17 +127,18 @@ var gPrivacyPane = {
    * Update the private browsing auto-start pref and the history mode
    * micro-management prefs based on the history mode menulist
    */
   updateHistoryModePrefs: function PPP_updateHistoryModePrefs()
   {
     let pref = document.getElementById("browser.privatebrowsing.autostart");
     switch (document.getElementById("historyMode").value) {
     case "remember":
-      pref.value = false;
+      if (pref.value)
+        pref.value = false;
 
       // select the remember history option if needed
       let rememberHistoryCheckbox = document.getElementById("rememberHistory");
       if (!rememberHistoryCheckbox.checked)
         rememberHistoryCheckbox.checked = true;
 
       // select the remember forms history option
       document.getElementById("browser.formfill.enable").value = true;
@@ -141,17 +147,18 @@ var gPrivacyPane = {
       document.getElementById("network.cookie.cookieBehavior").value = 0;
       // select the cookie lifetime policy option
       document.getElementById("network.cookie.lifetimePolicy").value = 0;
 
       // select the clear on close option
       document.getElementById("privacy.sanitize.sanitizeOnShutdown").value = false;
       break;
     case "dontremember":
-      pref.value = true;
+      if (!pref.value)
+        pref.value = true;
       break;
     }
   },
 
   /**
    * Update the privacy micro-management controls based on the
    * value of the private browsing auto-start checkbox.
    */
@@ -214,29 +221,75 @@ var gPrivacyPane = {
   },
 
   autoStartPrivateBrowsingObserver:
   {
     QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIObserver]),
 
     observe: function PPP_observe(aSubject, aTopic, aData)
     {
-      let privateBrowsingService = Components.classes["@mozilla.org/privatebrowsing;1"].
-        getService(Components.interfaces.nsIPrivateBrowsingService);
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+      if (!gPrivacyPane._shouldPromptForRestart) {
+        // We're performing a revert. Just let it happen.
+        gPrivacyPane._shouldPromptForRestart = true;
+        return;
+      }
 
+      const Cc = Components.classes, Ci = Components.interfaces;
+      let pref = document.getElementById("browser.privatebrowsing.autostart");
+      let brandName = document.getElementById("bundleBrand").getString("brandShortName");
+      let bundle = document.getElementById("bundlePreferences");
+      let msg = bundle.getFormattedString(pref.value ?
+                                          "featureEnableRequiresRestart" : "featureDisableRequiresRestart",
+                                          [brandName]);
+      let title = bundle.getFormattedString("shouldRestartTitle", [brandName]);
+      let prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);
+      let shouldProceed = prompts.confirm(window, title, msg)
+      if (shouldProceed) {
+        let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
+                           .createInstance(Ci.nsISupportsPRBool);
+        Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
+                                     "restart");
+        shouldProceed = !cancelQuit.data;
+
+        if (shouldProceed) {
+          let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
+                             .getService(Ci.nsIAppStartup);
+          appStartup.quit(Ci.nsIAppStartup.eAttemptQuit |  Ci.nsIAppStartup.eRestart);
+          return;
+        }
+      }
+      gPrivacyPane._shouldPromptForRestart = false;
+      pref.value = !pref.value;
+
+      let mode = document.getElementById("historyMode");
+      if (mode.value != "custom") {
+        mode.selectedIndex = pref.value ? 1 : 0;
+        mode.doCommand();
+      } else {
+        let rememberHistoryCheckbox = document.getElementById("rememberHistory");
+        rememberHistory.checked = pref.value;
+      }
+#else
       // Toggle the private browsing mode without switching the session
       let prefValue = document.getElementById("browser.privatebrowsing.autostart").value;
       let keepCurrentSession = document.getElementById("browser.privatebrowsing.keep_current_session");
       keepCurrentSession.value = true;
+
+      let privateBrowsingService = Components.classes["@mozilla.org/privatebrowsing;1"].
+        getService(Components.interfaces.nsIPrivateBrowsingService);
+
       // If activating from within the private browsing mode, reset the
       // private session
       if (prefValue && privateBrowsingService.privateBrowsingEnabled)
         privateBrowsingService.privateBrowsingEnabled = false;
       privateBrowsingService.privateBrowsingEnabled = prefValue;
+
       keepCurrentSession.reset();
+#endif
     }
   },
 
   // HISTORY
 
   /**
    * Read the location bar enabled and suggestion prefs
    * @return Int value for suggestion menulist
--- a/browser/components/preferences/in-content/tests/Makefile.in
+++ b/browser/components/preferences/in-content/tests/Makefile.in
@@ -10,29 +10,38 @@ relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
     head.js \
     browser_advanced_update.js \
     browser_bug410900.js \
-    browser_bug567487.js \
     browser_bug731866.js \
     browser_connection.js \
+    $(NULL)
+
+ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+_BROWSER_FILES += \
     privacypane_tests.js \
+    browser_bug567487.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_7.js \
+    browser_privacypane_8.js \
+    $(NULL)
+else
+_BROWSER_FILES += \
+    privacypane_tests_perwindow.js \
+    browser_privacypane_1.js \
+    browser_privacypane_3.js \
+    browser_privacypane_4.js \
+    browser_privacypane_5.js \
     browser_privacypane_8.js \
     $(NULL)
-
-ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
-_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/in-content/tests/browser_privacypane_1.js
+++ b/browser/components/preferences/in-content/tests/browser_privacypane_1.js
@@ -6,17 +6,21 @@ function test() {
                getService(Ci.mozIJSSubScriptLoader);
 
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_pane_visibility,
     test_dependent_elements,
     test_dependent_cookie_elements,
     test_dependent_clearonclose_elements,
     test_dependent_prefs,
 
--- a/browser/components/preferences/in-content/tests/browser_privacypane_2.js
+++ b/browser/components/preferences/in-content/tests/browser_privacypane_2.js
@@ -6,17 +6,21 @@ function test() {
                getService(Ci.mozIJSSubScriptLoader);
 
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_historymode_retention("remember", undefined),
     test_historymode_retention("dontremember", "remember"),
     test_historymode_retention("custom", "dontremember"),
     // custom without any micro-prefs changed won't retain
     test_historymode_retention("remember", "dontremember"),
     test_historymode_retention("custom", "remember"),
--- a/browser/components/preferences/in-content/tests/browser_privacypane_3.js
+++ b/browser/components/preferences/in-content/tests/browser_privacypane_3.js
@@ -5,17 +5,21 @@ function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_custom_retention("rememberHistory", "remember"),
     test_custom_retention("rememberHistory", "custom"),
     test_custom_retention("rememberForms", "remember"),
     test_custom_retention("rememberForms", "custom"),
     test_historymode_retention("remember", "remember"),
 
--- a/browser/components/preferences/in-content/tests/browser_privacypane_4.js
+++ b/browser/components/preferences/in-content/tests/browser_privacypane_4.js
@@ -5,17 +5,21 @@ function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_custom_retention("acceptCookies", "remember"),
     test_custom_retention("acceptCookies", "custom"),
     test_custom_retention("acceptThirdParty", "remember"),
     test_custom_retention("acceptThirdParty", "custom"),
     test_custom_retention("keepCookiesUntil", "remember", 1),
     test_custom_retention("keepCookiesUntil", "custom", 2),
--- a/browser/components/preferences/in-content/tests/browser_privacypane_5.js
+++ b/browser/components/preferences/in-content/tests/browser_privacypane_5.js
@@ -5,17 +5,21 @@ function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_locbar_suggestion_retention(-1, undefined),
     test_locbar_suggestion_retention(1, -1),
     test_locbar_suggestion_retention(2, 1),
     test_locbar_suggestion_retention(0, 2),
     test_locbar_suggestion_retention(0, 0),
 
--- a/browser/components/preferences/in-content/tests/browser_privacypane_6.js
+++ b/browser/components/preferences/in-content/tests/browser_privacypane_6.js
@@ -5,17 +5,21 @@ function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_privatebrowsing_toggle,
     enter_private_browsing, // once again, test with PB initially enabled
     test_privatebrowsing_toggle,
 
     // don't reset preferences, will pick up where we left off in browser_privacypane_7.js
   ]);
--- a/browser/components/preferences/in-content/tests/browser_privacypane_7.js
+++ b/browser/components/preferences/in-content/tests/browser_privacypane_7.js
@@ -5,17 +5,21 @@ function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_privatebrowsing_ui,
     enter_private_browsing, // once again, test with PB initially enabled
     test_privatebrowsing_ui,
 
     // reset all preferences to their default values once we're done
     reset_preferences
--- a/browser/components/preferences/in-content/tests/browser_privacypane_8.js
+++ b/browser/components/preferences/in-content/tests/browser_privacypane_8.js
@@ -5,17 +5,21 @@ function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     // history mode should be initialized to remember
     test_historymode_retention("remember", undefined),
 
     // history mode should remain remember; toggle acceptCookies checkbox
     test_custom_retention("acceptCookies", "remember"),
 
copy from browser/components/preferences/in-content/tests/privacypane_tests.js
copy to browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js
--- a/browser/components/preferences/in-content/tests/privacypane_tests.js
+++ b/browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js
@@ -17,20 +17,20 @@ function runTestOnPrivacyPrefPane(testFu
   
   gBrowser.selectedTab = gBrowser.addTab("about:preferences");
 }
 
 function controlChanged(element) {
   element.doCommand();
 }
 
+// We can only test the panes that don't trigger a preference update
 function test_pane_visibility(win) {
   let modes = {
     "remember": "historyRememberPane",
-    "dontremember": "historyDontRememberPane",
     "custom": "historyCustomPane"
   };
 
   let historymode = win.document.getElementById("historyMode");
   ok(historymode, "history mode menulist should exist");
   let historypane = win.document.getElementById("historyPane");
   ok(historypane, "history mode pane should exist");
 
@@ -111,40 +111,16 @@ function test_dependent_elements(win) {
   expect_disabled(false);
   check_independents(false);
 
   // setting the mode to custom shouldn't change anything
   historymode.value = "custom";
   controlChanged(historymode);
   expect_disabled(false);
   check_independents(false);
-
-  // controls should only change in custom mode
-  historymode.value = "dontremember";
-  controlChanged(historymode);
-  expect_disabled(false);
-  check_independents(false);
-
-  // controls should only change in custom mode
-  historymode.value = "custom";
-  controlChanged(historymode);
-  expect_disabled(true);
-  check_independents(false);
-
-  // dependent controls should follow pbautostart
-  pbautostart.checked = false;
-  controlChanged(pbautostart);
-  expect_disabled(false);
-  check_independents(false);
-
-  // dependent controls should follow pbautostart
-  pbautostart.checked = true;
-  controlChanged(pbautostart);
-  expect_disabled(true);
-  check_independents(false);
 }
 
 function test_dependent_cookie_elements(win) {
   let historymode = win.document.getElementById("historyMode");
   ok(historymode, "history mode menulist should exist");
   let pbautostart = win.document.getElementById("privateBrowsingAutoStart");
   ok(pbautostart, "the private browsing auto-start checkbox should exist");
   let controls = [
@@ -170,35 +146,21 @@ function test_dependent_cookie_elements(
   pbautostart.checked = false;
   controlChanged(pbautostart);
   expect_disabled(false);
 
   acceptcookies.checked = false;
   controlChanged(acceptcookies);
   expect_disabled(true);
 
-  // pbautostart shouldn't change anything now
-  pbautostart.checked = true;
-  controlChanged(pbautostart);
-  expect_disabled(true);
-
-  pbautostart.checked = false;
-  controlChanged(pbautostart);
-  expect_disabled(true);
-
   acceptcookies.checked = true;
   controlChanged(acceptcookies);
   expect_disabled(false);
 
   let accessthirdparty = controls.shift();
-  pbautostart.checked = true;
-  controlChanged(pbautostart);
-  expect_disabled(true);
-  ok(!accessthirdparty.disabled, "access third party button should be enabled");
-
   acceptcookies.checked = false;
   controlChanged(acceptcookies);
   expect_disabled(true);
   ok(accessthirdparty.disabled, "access third party button should be disabled");
 
   pbautostart.checked = false;
   controlChanged(pbautostart);
   expect_disabled(true);
@@ -232,24 +194,16 @@ function test_dependent_clearonclose_ele
   alwaysclear.checked = false;
   controlChanged(alwaysclear);
   expect_disabled(true);
 
   alwaysclear.checked = true;
   controlChanged(alwaysclear);
   expect_disabled(false);
 
-  pbautostart.checked = true;
-  controlChanged(pbautostart);
-  expect_disabled(true);
-
-  pbautostart.checked = false;
-  controlChanged(pbautostart);
-  expect_disabled(false);
-
   alwaysclear.checked = false;
   controlChanged(alwaysclear);
   expect_disabled(true);
 }
 
 function test_dependent_prefs(win) {
   let historymode = win.document.getElementById("historyMode");
   ok(historymode, "history mode menulist should exist");
@@ -288,16 +242,22 @@ function test_dependent_prefs(win) {
   expect_checked(true);
 }
 
 function test_historymode_retention(mode, expect) {
   return function(win) {
     let historymode = win.document.getElementById("historyMode");
     ok(historymode, "history mode menulist should exist");
 
+    if ((historymode.value == "remember" && mode == "dontremember") ||
+        (historymode.value == "dontremember" && mode == "remember") ||
+        (historymode.value == "custom" && mode == "dontremember")) {
+      return;
+    }
+
     if (expect !== undefined) {
       is(historymode.value, expect,
         "history mode is expected to remain " + expect);
     }
 
     historymode.value = mode;
     controlChanged(historymode);
   };
@@ -343,123 +303,16 @@ function test_locbar_suggestion_retentio
         "location bar suggestion is expected to remain " + expect);
     }
 
     locbarsuggest.value = mode;
     controlChanged(locbarsuggest);
   };
 }
 
-function test_privatebrowsing_toggle(win) {
-  let historymode = win.document.getElementById("historyMode");
-  ok(historymode, "history mode menulist should exist");
-  let pbautostart = win.document.getElementById("privateBrowsingAutoStart");
-  ok(pbautostart, "the private browsing auto-start checkbox should exist");
-
-  let pbService = Cc["@mozilla.org/privatebrowsing;1"].
-                  getService(Ci.nsIPrivateBrowsingService);
-
-  // initial state
-  historymode.value = "remember";
-  controlChanged(historymode);
-
-  // switch to dontremember mode
-  historymode.value = "dontremember";
-  controlChanged(historymode);
-  ok(pbService.privateBrowsingEnabled, "private browsing should be activated");
-
-  // switch to remember mode
-  historymode.value = "remember";
-  controlChanged(historymode);
-  ok(!pbService.privateBrowsingEnabled, "private browsing should be deactivated");
-
-  // switch to custom mode
-  historymode.value = "custom";
-  controlChanged(historymode);
-  ok(!pbService.privateBrowsingEnabled, "private browsing should remain deactivated");
-
-  // check the autostart checkbox
-  pbautostart.checked = true;
-  controlChanged(pbautostart);
-  ok(pbService.privateBrowsingEnabled, "private browsing should be activated");
-
-  // uncheck the autostart checkbox
-  pbautostart.checked = false;
-  controlChanged(pbautostart);
-  ok(!pbService.privateBrowsingEnabled, "private browsing should be deactivated");
-}
-
-function test_privatebrowsing_ui(win) {
-  let historymode = win.document.getElementById("historyMode");
-  ok(historymode, "history mode menulist should exist");
-  let pbautostart = win.document.getElementById("privateBrowsingAutoStart");
-  ok(pbautostart, "the private browsing auto-start checkbox should exist");
-
-  let pbmenuitem = document.getElementById("privateBrowsingItem");
-  ok(pbmenuitem, "the private browsing menu item should exist");
-  let pbcommand = document.getElementById("Tools:PrivateBrowsing");
-  ok(pbcommand, "the private browsing command should exist");
-
-  // initial state
-  historymode.value = "remember";
-  controlChanged(historymode);
-  ok(!pbmenuitem.hasAttribute("disabled"),
-    "private browsing menu item should not be initially disabled");
-  ok(!pbcommand.hasAttribute("disabled"),
-    "private browsing command should not be initially disabled");
-
-  // switch to dontremember mode
-  historymode.value = "dontremember";
-  controlChanged(historymode);
-  ok(pbmenuitem.hasAttribute("disabled"),
-    "private browsing menu item should be disabled");
-  ok(pbcommand.hasAttribute("disabled"),
-    "private browsing command should be disabled");
-
-  // switch to remember mode
-  historymode.value = "remember";
-  controlChanged(historymode);
-  ok(!pbmenuitem.hasAttribute("disabled"),
-    "private browsing menu item should be enabled");
-  ok(!pbcommand.hasAttribute("disabled"),
-    "private browsing command should be enabled");
-
-  // switch to custom mode
-  historymode.value = "custom";
-  controlChanged(historymode);
-  ok(!pbmenuitem.hasAttribute("disabled"),
-    "private browsing menu item should remain enabled");
-  ok(!pbcommand.hasAttribute("disabled"),
-    "private browsing command should remain enabled");
-
-  // check the autostart checkbox
-  pbautostart.checked = true;
-  controlChanged(pbautostart);
-  ok(pbmenuitem.hasAttribute("disabled"),
-    "private browsing menu item should be disabled");
-  ok(pbcommand.hasAttribute("disabled"),
-    "private browsing command should be disabled");
-
-  // uncheck the autostart checkbox
-  pbautostart.checked = false;
-  controlChanged(pbautostart);
-  ok(!pbmenuitem.hasAttribute("disabled"),
-    "private browsing menu item should be enabled");
-  ok(!pbcommand.hasAttribute("disabled"),
-    "private browsing command should be enabled");
-}
-
-function enter_private_browsing(win) {
-  let pbService = Cc["@mozilla.org/privatebrowsing;1"].
-                  getService(Ci.nsIPrivateBrowsingService);
-  win.document.getElementById("browser.privatebrowsing.keep_current_session")
-              .value = true;
-  pbService.privateBrowsingEnabled = true;
-}
-
 function reset_preferences(win) {
   let prefs = win.document.querySelectorAll("#privacyPreferences > preference");
   for (let i = 0; i < prefs.length; ++i)
     if (prefs[i].hasUserValue)
       prefs[i].reset();
 }
 
 let testRunner;
--- a/browser/components/preferences/main.js
+++ b/browser/components/preferences/main.js
@@ -18,22 +18,35 @@ var gMainPane = {
     this._pane = document.getElementById("paneMain");
 
     // set up the "use current page" label-changing listener
     this._updateUseCurrentButton();
     window.addEventListener("focus", this._updateUseCurrentButton.bind(this), false);
 
     this.updateBrowserStartupLastSession();
 
+    this.setupDownloadsWindowOptions();
+
     // Notify observers that the UI is now ready
     Components.classes["@mozilla.org/observer-service;1"]
               .getService(Components.interfaces.nsIObserverService)
               .notifyObservers(window, "main-pane-loaded", null);
   },
 
+  setupDownloadsWindowOptions: function ()
+  {
+    let showWhenDownloading = document.getElementById("showWhenDownloading");
+    let closeWhenDone = document.getElementById("closeWhenDone");
+
+    // These radio buttons should be hidden when the Downloads Panel is enabled.
+    let shouldHide = !DownloadsCommon.useToolkitUI;
+    showWhenDownloading.hidden = shouldHide;
+    closeWhenDone.hidden = shouldHide;
+  },
+
   // HOME PAGE
 
   /*
    * Preferences:
    *
    * browser.startup.homepage
    * - the user's home page, as a string; if the home page is a set of tabs,
    *   this will be those URLs separated by the pipe character "|"
--- a/browser/components/preferences/privacy.js
+++ b/browser/components/preferences/privacy.js
@@ -8,16 +8,21 @@ Components.utils.import("resource://gre/
 var gPrivacyPane = {
 
   /**
    * Whether the use has selected the auto-start private browsing mode in the UI.
    */
   _autoStartPrivateBrowsing: false,
 
   /**
+   * Whether the prompt to restart Firefox should appear when changing the autostart pref.
+   */
+  _shouldPromptForRestart: true,
+
+  /**
    * Sets up the UI for the number of days of history to keep, and updates the
    * label of the "Clear Now..." button.
    */
   init: function ()
   {
     this._updateSanitizeSettingsButton();
     this.initializeHistoryMode();
     this.updateHistoryModePane();
@@ -125,17 +130,18 @@ var gPrivacyPane = {
    * Update the private browsing auto-start pref and the history mode
    * micro-management prefs based on the history mode menulist
    */
   updateHistoryModePrefs: function PPP_updateHistoryModePrefs()
   {
     let pref = document.getElementById("browser.privatebrowsing.autostart");
     switch (document.getElementById("historyMode").value) {
     case "remember":
-      pref.value = false;
+      if (pref.value)
+        pref.value = false;
 
       // select the remember history option if needed
       let rememberHistoryCheckbox = document.getElementById("rememberHistory");
       if (!rememberHistoryCheckbox.checked)
         rememberHistoryCheckbox.checked = true;
 
       // select the remember forms history option
       document.getElementById("browser.formfill.enable").value = true;
@@ -144,17 +150,18 @@ var gPrivacyPane = {
       document.getElementById("network.cookie.cookieBehavior").value = 0;
       // select the cookie lifetime policy option
       document.getElementById("network.cookie.lifetimePolicy").value = 0;
 
       // select the clear on close option
       document.getElementById("privacy.sanitize.sanitizeOnShutdown").value = false;
       break;
     case "dontremember":
-      pref.value = true;
+      if (!pref.value)
+        pref.value = true;
       break;
     }
   },
 
   /**
    * Update the privacy micro-management controls based on the
    * value of the private browsing auto-start checkbox.
    */
@@ -217,33 +224,75 @@ var gPrivacyPane = {
   },
 
   autoStartPrivateBrowsingObserver:
   {
     QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIObserver]),
 
     observe: function PPP_observe(aSubject, aTopic, aData)
     {
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+      if (!gPrivacyPane._shouldPromptForRestart) {
+        // We're performing a revert. Just let it happen.
+        gPrivacyPane._shouldPromptForRestart = true;
+        return;
+      }
+
+      const Cc = Components.classes, Ci = Components.interfaces;
+      let pref = document.getElementById("browser.privatebrowsing.autostart");
+      let brandName = document.getElementById("bundleBrand").getString("brandShortName");
+      let bundle = document.getElementById("bundlePreferences");
+      let msg = bundle.getFormattedString(pref.value ?
+                                          "featureEnableRequiresRestart" : "featureDisableRequiresRestart",
+                                          [brandName]);
+      let title = bundle.getFormattedString("shouldRestartTitle", [brandName]);
+      let prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);
+      let shouldProceed = prompts.confirm(window, title, msg)
+      if (shouldProceed) {
+        let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
+                           .createInstance(Ci.nsISupportsPRBool);
+        Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
+                                     "restart");
+        shouldProceed = !cancelQuit.data;
+
+        if (shouldProceed) {
+          let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
+                             .getService(Ci.nsIAppStartup);
+          appStartup.quit(Ci.nsIAppStartup.eAttemptQuit |  Ci.nsIAppStartup.eRestart);
+          return;
+        }
+      }
+      gPrivacyPane._shouldPromptForRestart = false;
+      pref.value = !pref.value;
+
+      let mode = document.getElementById("historyMode");
+      if (mode.value != "custom") {
+        mode.selectedIndex = pref.value ? 1 : 0;
+        mode.doCommand();
+      } else {
+        let rememberHistoryCheckbox = document.getElementById("rememberHistory");
+        rememberHistory.checked = pref.value;
+      }
+#else
       // Toggle the private browsing mode without switching the session
       let prefValue = document.getElementById("browser.privatebrowsing.autostart").value;
       let keepCurrentSession = document.getElementById("browser.privatebrowsing.keep_current_session");
       keepCurrentSession.value = true;
 
-#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
       let privateBrowsingService = Components.classes["@mozilla.org/privatebrowsing;1"].
         getService(Components.interfaces.nsIPrivateBrowsingService);
 
       // If activating from within the private browsing mode, reset the
       // private session
       if (prefValue && privateBrowsingService.privateBrowsingEnabled)
         privateBrowsingService.privateBrowsingEnabled = false;
       privateBrowsingService.privateBrowsingEnabled = prefValue;
-#endif
 
       keepCurrentSession.reset();
+#endif
     }
   },
 
   // HISTORY
 
   /**
    * Read the location bar enabled and suggestion prefs
    * @return Int value for suggestion menulist
--- a/browser/components/preferences/tests/Makefile.in
+++ b/browser/components/preferences/tests/Makefile.in
@@ -9,30 +9,39 @@ VPATH		= @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
     browser_advanced_update.js \
     browser_bug410900.js \
-    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_8.js \
     browser_permissions.js \
     browser_chunk_permissions.js \
     $(NULL)
 
 ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
 _BROWSER_FILES += \
+    privacypane_tests.js \
+    browser_bug567487.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_7.js \
+    browser_privacypane_8.js \
+    $(NULL)
+else
+_BROWSER_FILES += \
+    privacypane_tests_perwindow.js \
+    browser_privacypane_1.js \
+    browser_privacypane_3.js \
+    browser_privacypane_4.js \
+    browser_privacypane_5.js \
+    browser_privacypane_8.js \
     $(NULL)
 endif
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/components/preferences/tests/browser_privacypane_1.js
+++ b/browser/components/preferences/tests/browser_privacypane_1.js
@@ -7,17 +7,21 @@ function test() {
                getService(Ci.mozIJSSubScriptLoader);
 
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_pane_visibility,
     test_dependent_elements,
     test_dependent_cookie_elements,
     test_dependent_clearonclose_elements,
     test_dependent_prefs,
 
--- a/browser/components/preferences/tests/browser_privacypane_2.js
+++ b/browser/components/preferences/tests/browser_privacypane_2.js
@@ -7,17 +7,21 @@ function test() {
                getService(Ci.mozIJSSubScriptLoader);
 
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_historymode_retention("remember", undefined),
     test_historymode_retention("dontremember", "remember"),
     test_historymode_retention("custom", "dontremember"),
     // custom without any micro-prefs changed won't retain
     test_historymode_retention("remember", "dontremember"),
     test_historymode_retention("custom", "remember"),
--- a/browser/components/preferences/tests/browser_privacypane_3.js
+++ b/browser/components/preferences/tests/browser_privacypane_3.js
@@ -6,17 +6,21 @@ function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_custom_retention("rememberHistory", "remember"),
     test_custom_retention("rememberHistory", "custom"),
     test_custom_retention("rememberForms", "remember"),
     test_custom_retention("rememberForms", "custom"),
     test_historymode_retention("remember", "remember"),
 
--- a/browser/components/preferences/tests/browser_privacypane_4.js
+++ b/browser/components/preferences/tests/browser_privacypane_4.js
@@ -6,17 +6,21 @@ function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_custom_retention("acceptCookies", "remember"),
     test_custom_retention("acceptCookies", "custom"),
     test_custom_retention("acceptThirdParty", "remember"),
     test_custom_retention("acceptThirdParty", "custom"),
     test_custom_retention("keepCookiesUntil", "remember", 1),
     test_custom_retention("keepCookiesUntil", "custom", 2),
--- a/browser/components/preferences/tests/browser_privacypane_5.js
+++ b/browser/components/preferences/tests/browser_privacypane_5.js
@@ -6,17 +6,21 @@ function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_locbar_suggestion_retention(-1, undefined),
     test_locbar_suggestion_retention(1, -1),
     test_locbar_suggestion_retention(2, 1),
     test_locbar_suggestion_retention(0, 2),
     test_locbar_suggestion_retention(0, 0),
 
--- a/browser/components/preferences/tests/browser_privacypane_6.js
+++ b/browser/components/preferences/tests/browser_privacypane_6.js
@@ -6,17 +6,21 @@ function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_privatebrowsing_toggle,
     enter_private_browsing, // once again, test with PB initially enabled
     test_privatebrowsing_toggle,
 
     // don't reset preferences, will pick up where we left off in browser_privacypane_7.js
   ]);
--- a/browser/components/preferences/tests/browser_privacypane_7.js
+++ b/browser/components/preferences/tests/browser_privacypane_7.js
@@ -6,17 +6,21 @@ function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     test_privatebrowsing_ui,
     enter_private_browsing, // once again, test with PB initially enabled
     test_privatebrowsing_ui,
 
     // reset all preferences to their default values once we're done
     reset_preferences
--- a/browser/components/preferences/tests/browser_privacypane_8.js
+++ b/browser/components/preferences/tests/browser_privacypane_8.js
@@ -6,17 +6,21 @@ function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
-  loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  try {
+    loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
+  } catch(x) {
+    loader.loadSubScript(rootDir + "privacypane_tests.js", this);
+  }
 
   run_test_subset([
     // history mode should be initialized to remember
     test_historymode_retention("remember", undefined),
 
     // history mode should remain remember; toggle acceptCookies checkbox
     test_custom_retention("acceptCookies", "remember"),
 
copy from browser/components/preferences/tests/privacypane_tests.js
copy to browser/components/preferences/tests/privacypane_tests_perwindow.js
--- a/browser/components/preferences/tests/privacypane_tests.js
+++ b/browser/components/preferences/tests/privacypane_tests_perwindow.js
@@ -27,20 +27,20 @@ function runTestOnPrivacyPrefPane(testFu
   let dialog = openDialog("chrome://browser/content/preferences/preferences.xul", "Preferences",
                           "chrome,titlebar,toolbar,centerscreen,dialog=no", "panePrivacy");
 }
 
 function controlChanged(element) {
   element.doCommand();
 }
 
+// We can only test the panes that don't trigger a preference update
 function test_pane_visibility(win) {
   let modes = {
     "remember": "historyRememberPane",
-    "dontremember": "historyDontRememberPane",
     "custom": "historyCustomPane"
   };
 
   let historymode = win.document.getElementById("historyMode");
   ok(historymode, "history mode menulist should exist");
   let historypane = win.document.getElementById("historyPane");
   ok(historypane, "history mode pane should exist");
 
@@ -120,40 +120,16 @@ function test_dependent_elements(win) {
   expect_disabled(false);
   check_independents(false);
 
   // setting the mode to custom shouldn't change anything
   historymode.value = "custom";
   controlChanged(historymode);
   expect_disabled(false);
   check_independents(false);
-
-  // controls should only change in custom mode
-  historymode.value = "dontremember";
-  controlChanged(historymode);
-  expect_disabled(false);
-  check_independents(false);
-
-  // controls should only change in custom mode
-  historymode.value = "custom";
-  controlChanged(historymode);
-  expect_disabled(true);
-  check_independents(false);
-
-  // dependent controls should follow pbautostart
-  pbautostart.checked = false;
-  controlChanged(pbautostart);
-  expect_disabled(false);
-  check_independents(false);
-
-  // dependent controls should follow pbautostart
-  pbautostart.checked = true;
-  controlChanged(pbautostart);
-  expect_disabled(true);
-  check_independents(false);
 }
 
 function test_dependent_cookie_elements(win) {
   let historymode = win.document.getElementById("historyMode");
   ok(historymode, "history mode menulist should exist");
   let pbautostart = win.document.getElementById("privateBrowsingAutoStart");
   ok(pbautostart, "the private browsing auto-start checkbox should exist");
   let controls = [
@@ -179,35 +155,21 @@ function test_dependent_cookie_elements(
   pbautostart.checked = false;
   controlChanged(pbautostart);
   expect_disabled(false);
 
   acceptcookies.checked = false;
   controlChanged(acceptcookies);
   expect_disabled(true);
 
-  // pbautostart shouldn't change anything now
-  pbautostart.checked = true;
-  controlChanged(pbautostart);
-  expect_disabled(true);
-
-  pbautostart.checked = false;
-  controlChanged(pbautostart);
-  expect_disabled(true);
-
   acceptcookies.checked = true;
   controlChanged(acceptcookies);
   expect_disabled(false);
 
   let accessthirdparty = controls.shift();
-  pbautostart.checked = true;
-  controlChanged(pbautostart);
-  expect_disabled(true);
-  ok(!accessthirdparty.disabled, "access third party button should be enabled");
-
   acceptcookies.checked = false;
   controlChanged(acceptcookies);
   expect_disabled(true);
   ok(accessthirdparty.disabled, "access third party button should be disabled");
 
   pbautostart.checked = false;
   controlChanged(pbautostart);
   expect_disabled(true);
@@ -241,24 +203,16 @@ function test_dependent_clearonclose_ele
   alwaysclear.checked = false;
   controlChanged(alwaysclear);
   expect_disabled(true);
 
   alwaysclear.checked = true;
   controlChanged(alwaysclear);
   expect_disabled(false);
 
-  pbautostart.checked = true;
-  controlChanged(pbautostart);
-  expect_disabled(true);
-
-  pbautostart.checked = false;
-  controlChanged(pbautostart);
-  expect_disabled(false);
-
   alwaysclear.checked = false;
   controlChanged(alwaysclear);
   expect_disabled(true);
 }
 
 function test_dependent_prefs(win) {
   let historymode = win.document.getElementById("historyMode");
   ok(historymode, "history mode menulist should exist");
@@ -297,16 +251,22 @@ function test_dependent_prefs(win) {
   expect_checked(true);
 }
 
 function test_historymode_retention(mode, expect) {
   return function(win) {
     let historymode = win.document.getElementById("historyMode");
     ok(historymode, "history mode menulist should exist");
 
+    if ((historymode.value == "remember" && mode == "dontremember") ||
+        (historymode.value == "dontremember" && mode == "remember") ||
+        (historymode.value == "custom" && mode == "dontremember")) {
+      return;
+    }
+
     if (expect !== undefined) {
       is(historymode.value, expect,
         "history mode is expected to remain " + expect);
     }
 
     historymode.value = mode;
     controlChanged(historymode);
   };
@@ -352,123 +312,16 @@ function test_locbar_suggestion_retentio
         "location bar suggestion is expected to remain " + expect);
     }
 
     locbarsuggest.value = mode;
     controlChanged(locbarsuggest);
   };
 }
 
-function test_privatebrowsing_toggle(win) {
-  let historymode = win.document.getElementById("historyMode");
-  ok(historymode, "history mode menulist should exist");
-  let pbautostart = win.document.getElementById("privateBrowsingAutoStart");
-  ok(pbautostart, "the private browsing auto-start checkbox should exist");
-
-  let pbService = Cc["@mozilla.org/privatebrowsing;1"].
-                  getService(Ci.nsIPrivateBrowsingService);
-
-  // initial state
-  historymode.value = "remember";
-  controlChanged(historymode);
-
-  // switch to dontremember mode
-  historymode.value = "dontremember";
-  controlChanged(historymode);
-  ok(pbService.privateBrowsingEnabled, "private browsing should be activated");
-
-  // switch to remember mode
-  historymode.value = "remember";
-  controlChanged(historymode);
-  ok(!pbService.privateBrowsingEnabled, "private browsing should be deactivated");
-
-  // switch to custom mode
-  historymode.value = "custom";
-  controlChanged(historymode);
-  ok(!pbService.privateBrowsingEnabled, "private browsing should remain deactivated");
-
-  // check the autostart checkbox
-  pbautostart.checked = true;
-  controlChanged(pbautostart);
-  ok(pbService.privateBrowsingEnabled, "private browsing should be activated");
-
-  // uncheck the autostart checkbox
-  pbautostart.checked = false;
-  controlChanged(pbautostart);
-  ok(!pbService.privateBrowsingEnabled, "private browsing should be deactivated");
-}
-
-function test_privatebrowsing_ui(win) {
-  let historymode = win.document.getElementById("historyMode");
-  ok(historymode, "history mode menulist should exist");
-  let pbautostart = win.document.getElementById("privateBrowsingAutoStart");
-  ok(pbautostart, "the private browsing auto-start checkbox should exist");
-
-  let pbmenuitem = document.getElementById("privateBrowsingItem");
-  ok(pbmenuitem, "the private browsing menu item should exist");
-  let pbcommand = document.getElementById("Tools:PrivateBrowsing");
-  ok(pbcommand, "the private browsing command should exist");
-
-  // initial state
-  historymode.value = "remember";
-  controlChanged(historymode);
-  ok(!pbmenuitem.hasAttribute("disabled"),
-    "private browsing menu item should not be initially disabled");
-  ok(!pbcommand.hasAttribute("disabled"),
-    "private browsing command should not be initially disabled");
-
-  // switch to dontremember mode
-  historymode.value = "dontremember";
-  controlChanged(historymode);
-  ok(pbmenuitem.hasAttribute("disabled"),
-    "private browsing menu item should be disabled");
-  ok(pbcommand.hasAttribute("disabled"),
-    "private browsing command should be disabled");
-
-  // switch to remember mode
-  historymode.value = "remember";
-  controlChanged(historymode);
-  ok(!pbmenuitem.hasAttribute("disabled"),
-    "private browsing menu item should be enabled");
-  ok(!pbcommand.hasAttribute("disabled"),
-    "private browsing command should be enabled");
-
-  // switch to custom mode
-  historymode.value = "custom";
-  controlChanged(historymode);
-  ok(!pbmenuitem.hasAttribute("disabled"),
-    "private browsing menu item should remain enabled");
-  ok(!pbcommand.hasAttribute("disabled"),
-    "private browsing command should remain enabled");
-
-  // check the autostart checkbox
-  pbautostart.checked = true;
-  controlChanged(pbautostart);
-  ok(pbmenuitem.hasAttribute("disabled"),
-    "private browsing menu item should be disabled");
-  ok(pbcommand.hasAttribute("disabled"),
-    "private browsing command should be disabled");
-
-  // uncheck the autostart checkbox
-  pbautostart.checked = false;
-  controlChanged(pbautostart);
-  ok(!pbmenuitem.hasAttribute("disabled"),
-    "private browsing menu item should be enabled");
-  ok(!pbcommand.hasAttribute("disabled"),
-    "private browsing command should be enabled");
-}
-
-function enter_private_browsing(win) {
-  let pbService = Cc["@mozilla.org/privatebrowsing;1"].
-                  getService(Ci.nsIPrivateBrowsingService);
-  win.document.getElementById("browser.privatebrowsing.keep_current_session")
-              .value = true;
-  pbService.privateBrowsingEnabled = true;
-}
-
 function reset_preferences(win) {
   let prefs = win.document.getElementsByTagName("preference");
   for (let i = 0; i < prefs.length; ++i)
     if (prefs[i].hasUserValue)
       prefs[i].reset();
 }
 
 let testRunner;
--- a/browser/components/privatebrowsing/Makefile.in
+++ b/browser/components/privatebrowsing/Makefile.in
@@ -6,13 +6,15 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = privatebrowsing
 
+ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
 DIRS = src
+endif
 
 TEST_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_DownloadLastDirWithCPS.js
+++ b/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_DownloadLastDirWithCPS.js
@@ -1,16 +1,17 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 let gTests;
 function test() {
   waitForExplicitFinish();
+  requestLongerTimeout(2);
   gTests = runTest();
   moveAlong();
 }
 
 function moveAlong() {
   try {
     gTests.next();
   } catch (x if x instanceof StopIteration) {
--- a/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_cookieacceptdialog.js
+++ b/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_cookieacceptdialog.js
@@ -30,17 +30,17 @@ function test() {
           if (expectedDisabled)
             is(remember.getAttribute("disabled"), "true",
                "The checkbox should be disabled");
           else
             ok(!remember.hasAttribute("disabled"),
                "The checkbox should not be disabled");
 
           win.close();
-          callback();
+          executeSoon(callback);
         });
       }, false);
     }
     Services.ww.registerNotification(observer);
 
     let remember = {};
     const time = (new Date("Jan 1, 2030")).getTime() / 1000;
     let cookie = {
--- a/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_crh.js
+++ b/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_crh.js
@@ -13,17 +13,17 @@ function test() {
       let crhCommand = aWindow.document.getElementById("Tools:Sanitize");
       ok(crhCommand, "The clear recent history command should exist");
 
       is(PrivateBrowsingUtils.isWindowPrivate(aWindow), aPrivateMode,
         "PrivateBrowsingUtils should report the correct per-window private browsing status");
       is(crhCommand.hasAttribute("disabled"), aPrivateMode,
         "Clear Recent History command should be disabled according to the private browsing mode");
 
-      aCallback();
+      executeSoon(aCallback);
     });
   };
 
   let windowsToClose = [];
   function testOnWindow(options, callback) {
     let win = OpenBrowserWindow(options);
     win.addEventListener("load", function onLoad() {
       win.removeEventListener("load", onLoad, false);
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -3723,16 +3723,19 @@ let SessionStoreInternal = {
       return;
     }
 
 #ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
     // Forget about private windows.
     for (let i = oState.windows.length - 1; i >= 0; i--) {
       if (oState.windows[i].isPrivate) {
         oState.windows.splice(i, 1);
+        if (oState.selectedWindow >= i) {
+          oState.selectedWindow--;
+        }
       }
     }
     for (let i = oState._closedWindows.length - 1; i >= 0; i--) {
       if (oState._closedWindows[i].isPrivate) {
         oState._closedWindows.splice(i, 1);
       }
     }
 #endif
--- a/browser/components/sessionstore/test/Makefile.in
+++ b/browser/components/sessionstore/test/Makefile.in
@@ -136,16 +136,17 @@ MOCHITEST_BROWSER_FILES = \
 	$(filter disabled-for-intermittent-failures--bug-766044, browser_459906_sample.html) \
 	$(filter disabled-for-intermittent-failures--bug-765389, browser_461743_sample.html) \
 	$(NULL)
 
 ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
 MOCHITEST_BROWSER_FILES += \
 	browser_354894_perwindowpb.js \
 	browser_394759_perwindowpb.js \
+	browser_819510_perwindowpb.js \
 	$(NULL)
 else
 MOCHITEST_BROWSER_FILES += \
 	browser_248970_a.js \
 	browser_248970_b.js \
 	browser_248970_b_perwindowpb.js \
 	browser_354894.js \
 	browser_394759_privatebrowsing.js \
--- a/browser/components/sessionstore/test/browser_586068-apptabs.js
+++ b/browser/components/sessionstore/test/browser_586068-apptabs.js
@@ -33,17 +33,17 @@ function test() {
 
     // get the tab
     let tab;
     for (let i = 0; i < window.gBrowser.tabs.length; i++) {
       if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
         tab = window.gBrowser.tabs[i];
     }
 
-    ok(tab.pinned || gBrowser.selectedTab == tab,
+    ok(tab.pinned || tab.selected,
        "load came from pinned or selected tab");
 
     // We should get 4 loads: 3 app tabs + 1 normal selected tab
     if (loadCount < 4)
       return;
 
     gProgressListener.unsetCallback();
     executeSoon(function () {
--- a/browser/components/sessionstore/test/browser_586068-apptabs_ondemand.js
+++ b/browser/components/sessionstore/test/browser_586068-apptabs_ondemand.js
@@ -32,17 +32,17 @@ function test() {
     // get the tab
     let tab;
     for (let i = 0; i < window.gBrowser.tabs.length; i++) {
       if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
         tab = window.gBrowser.tabs[i];
     }
 
     // Check that the load only comes from the selected tab.
-    ok(gBrowser.selectedTab == tab, "load came from selected tab");
+    ok(tab.selected, "load came from selected tab");
     is(aNeedRestore, 6, "six tabs left to restore");
     is(aRestoring, 1, "one tab is restoring");
     is(aRestored, 0, "no tabs have been restored, yet");
 
     gProgressListener.unsetCallback();
     executeSoon(function () {
       waitForBrowserState(JSON.parse(stateBackup), finish);
     });
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_819510_perwindowpb.js
@@ -0,0 +1,184 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const originalState = ss.getBrowserState();
+
+/** Private Browsing Test for Bug 819510 **/
+function test() {
+  waitForExplicitFinish();
+
+  registerCleanupFunction(function() {
+    ss.setBrowserState(originalState);
+  });
+
+  runNextTest();
+}
+
+let tests = [test_1, test_2, test_3 ];
+
+const testState = {
+  windows: [{
+    tabs: [
+      { entries: [{ url: "about:blank" }] },
+    ]
+  }]
+};
+
+function runNextTest() {
+  // Set an empty state
+  closeAllButPrimaryWindow();
+
+  // Run the next test, or finish
+  if (tests.length) {
+    let currentTest = tests.shift();
+    waitForBrowserState(testState, currentTest);
+  } else {
+    finish();
+  }
+}
+
+// Test opening default mochitest-normal-private-normal-private windows
+// (saving the state with last window being private)
+function test_1() {
+  testOnWindow(false, function(aWindow) {
+    aWindow.gBrowser.addTab("http://www.example.com/1");
+    testOnWindow(true, function(aWindow) {
+      aWindow.gBrowser.addTab("http://www.example.com/2");
+      testOnWindow(false, function(aWindow) {
+        aWindow.gBrowser.addTab("http://www.example.com/3");
+        testOnWindow(true, function(aWindow) {
+          aWindow.gBrowser.addTab("http://www.example.com/4");
+
+          let curState = JSON.parse(ss.getBrowserState());
+          is (curState.windows.length, 5, "Browser has opened 5 windows");
+          is (curState.windows[2].isPrivate, true, "Window is private");
+          is (curState.windows[4].isPrivate, true, "Last window is private");
+          is (curState.selectedWindow, 5, "Last window opened is the one selected");
+
+          forceWriteState(function(state) {
+            is(state.windows.length, 3,
+               "sessionstore state: 3 windows in data being written to disk");
+            is (state.selectedWindow, 3,
+               "Selected window is updated to match one of the saved windows");
+            state.windows.forEach(function(win) {
+              is(!win.isPrivate, true, "Saved window is not private");
+            });
+            is(state._closedWindows.length, 0,
+               "sessionstore state: no closed windows in data being written to disk");
+            runNextTest();
+          });
+        });
+      });
+    });
+  });
+}
+
+// Test opening default mochitest window + 2 private windows
+function test_2() {
+  testOnWindow(true, function(aWindow) {
+    aWindow.gBrowser.addTab("http://www.example.com/1");
+    testOnWindow(true, function(aWindow) {
+      aWindow.gBrowser.addTab("http://www.example.com/2");
+
+      let curState = JSON.parse(ss.getBrowserState());
+      is (curState.windows.length, 3, "Browser has opened 3 windows");
+      is (curState.windows[1].isPrivate, true, "Window 1 is private");
+      is (curState.windows[2].isPrivate, true, "Window 2 is private");
+      is (curState.selectedWindow, 3, "Last window opened is the one selected");
+
+      forceWriteState(function(state) {
+        is(state.windows.length, 1,
+           "sessionstore state: 1 windows in data being writted to disk");
+        is (state.selectedWindow, 1,
+           "Selected window is updated to match one of the saved windows");
+        is(state._closedWindows.length, 0,
+           "sessionstore state: no closed windows in data being writted to disk");
+        runNextTest();
+      });
+    });
+  });
+}
+
+// Test opening default-normal-private-normal windows and closing a normal window
+function test_3() {
+  testOnWindow(false, function(normalWindow) {
+    waitForTabLoad(normalWindow, "http://www.example.com/", function() {
+      testOnWindow(true, function(aWindow) {
+        waitForTabLoad(aWindow, "http://www.example.com/", function() {
+          testOnWindow(false, function(aWindow) {
+            waitForTabLoad(aWindow, "http://www.example.com/", function() {
+
+              let curState = JSON.parse(ss.getBrowserState());
+              is(curState.windows.length, 4, "Browser has opened 4 windows");
+              is(curState.windows[2].isPrivate, true, "Window 2 is private");
+              is(curState.selectedWindow, 4, "Last window opened is the one selected");
+
+              waitForWindowClose(normalWindow, function() {
+                forceWriteState(function(state) {
+                  is(state.windows.length, 2,
+                     "sessionstore state: 2 windows in data being writted to disk");
+                  is(state.selectedWindow, 2,
+                     "Selected window is updated to match one of the saved windows");
+                  state.windows.forEach(function(win) {
+                    is(!win.isPrivate, true, "Saved window is not private");
+                  });
+                  is(state._closedWindows.length, 1,
+                     "sessionstore state: 1 closed window in data being writted to disk");
+                  state._closedWindows.forEach(function(win) {
+                    is(!win.isPrivate, true, "Closed window is not private");
+                  });
+                  runNextTest();
+                });
+              });
+            });
+          });
+        });
+      });
+    });
+  });
+}
+
+function waitForWindowClose(aWin, aCallback) {
+  let winCount = JSON.parse(ss.getBrowserState()).windows.length;
+  aWin.addEventListener("SSWindowClosing", function onWindowClosing() {
+    aWin.removeEventListener("SSWindowClosing", onWindowClosing, false);
+    function checkCount() {
+      let state = JSON.parse(ss.getBrowserState());
+      if (state.windows.length == (winCount - 1)) {
+        aCallback();
+      } else {
+        executeSoon(checkCount);
+      }
+    }
+    executeSoon(checkCount);
+  }, false);
+  aWin.close();
+}
+
+function forceWriteState(aCallback) {
+  Services.obs.addObserver(function observe(aSubject, aTopic, aData) {
+    if (aTopic == "sessionstore-state-write") {
+      Services.obs.removeObserver(observe, aTopic);
+      Services.prefs.clearUserPref("browser.sessionstore.interval");
+      aSubject.QueryInterface(Ci.nsISupportsString);
+      aCallback(JSON.parse(aSubject.data));
+    }
+  }, "sessionstore-state-write", false);
+  Services.prefs.setIntPref("browser.sessionstore.interval", 0);
+}
+
+function testOnWindow(aIsPrivate, aCallback) {
+  let win = OpenBrowserWindow({private: aIsPrivate});
+  win.addEventListener("load", function onLoad() {
+    win.removeEventListener("load", onLoad, false);
+    executeSoon(function() { aCallback(win); });
+  }, false);
+}
+
+function waitForTabLoad(aWin, aURL, aCallback) {
+  aWin.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+    aWin.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+    aCallback();
+  }, true);
+  aWin.gBrowser.selectedBrowser.loadURI(aURL);
+}
--- a/browser/components/tabview/groupitems.js
+++ b/browser/components/tabview/groupitems.js
@@ -999,17 +999,17 @@ GroupItem.prototype = Utils.extend(new I
         if (typeof item.setResizable == 'function')
           item.setResizable(false, options.immediately);
 
         if (item == UI.getActiveTab() || !this._activeTab)
           this.setActiveTab(item);
 
         // if it matches the selected tab or no active tab and the browser
         // tab is hidden, the active group item would be set.
-        if (item.tab == gBrowser.selectedTab ||
+        if (item.tab.selected ||
             (!GroupItems.getActiveGroupItem() && !item.tab.hidden))
           UI.setActive(this);
       }
 
       if (!options.dontArrange)
         this.arrange({animate: !options.immediately});
 
       this._unfreezeItemSize({dontArrange: true});
@@ -2537,17 +2537,17 @@ let GroupItems = {
     if (tab._tabViewTabItem.parent && tab._tabViewTabItem.parent.id == groupItemId)
       return;
 
     let shouldUpdateTabBar = false;
     let shouldShowTabView = false;
     let groupItem;
 
     // switch to the appropriate tab first.
-    if (gBrowser.selectedTab == tab) {
+    if (tab.selected) {
       if (gBrowser.visibleTabs.length > 1) {
         gBrowser._blurTab(tab);
         shouldUpdateTabBar = true;
       } else {
         shouldShowTabView = true;
       }
     } else {
       shouldUpdateTabBar = true
--- a/browser/components/tabview/tabitems.js
+++ b/browser/components/tabview/tabitems.js
@@ -272,17 +272,17 @@ TabItem.prototype = Utils.extend(new Ite
         groupItem.add(this, {immediately: true});
 
         // restore the active tab for each group between browser sessions
         if (tabData.active)
           groupItem.setActiveTab(this);
 
         // if it matches the selected tab or no active tab and the browser
         // tab is hidden, the active group item would be set.
-        if (this.tab == gBrowser.selectedTab ||
+        if (this.tab.selected ||
             (!GroupItems.getActiveGroupItem() && !this.tab.hidden))
           UI.setActive(this.parent);
       }
     } else {
       if (options && options.groupItemId)
         groupItem = GroupItems.groupItem(options.groupItemId);
 
       if (groupItem) {
@@ -534,21 +534,21 @@ TabItem.prototype = Utils.extend(new Ite
     let tab = this.tab;
 
     function onZoomDone() {
       $canvas.css({ 'transform': null });
       $tabEl.removeClass("front");
 
       UI.goToTab(tab);
 
-      // tab might not be selected because hideTabView() is invoked after 
+      // tab might not be selected because hideTabView() is invoked after
       // UI.goToTab() so we need to setup everything for the gBrowser.selectedTab
-      if (tab != gBrowser.selectedTab) {
+      if (!tab.selected) {
         UI.onTabSelect(gBrowser.selectedTab);
-      } else { 
+      } else {
         if (isNewBlankTab)
           gWindow.gURLBar.focus();
       }
       if (self.parent && self.parent.expanded)
         self.parent.collapse();
 
       self._sendToSubscribers("zoomedIn");
     }
--- a/browser/components/tabview/test/browser_tabview_exit_button.js
+++ b/browser/components/tabview/test/browser_tabview_exit_button.js
@@ -29,37 +29,37 @@ function onTabViewLoadedAndShown() {
   ok(!originalTab.pinned, "the original tab is not an app tab");
 
   // create an app tab
   appTab = gBrowser.loadOneTab("about:blank");
   is(gBrowser.tabs.length, 2, "we now have two tabs");
   gBrowser.pinTab(appTab);
 
   // verify that the normal tab is selected
-  ok(gBrowser.selectedTab == originalTab, "the normal tab is selected");
+  ok(originalTab.selected, "the normal tab is selected");
 
   // hit the exit button for the first time
   exitButton = contentWindow.document.getElementById("exit-button");
   ok(exitButton, "Exit button exists");
 
   window.addEventListener("tabviewhidden", onTabViewHiddenForNormalTab, false);
   EventUtils.sendMouseEvent({ type: "click" }, exitButton, contentWindow);
 }
 
 // ----------
 function onTabViewHiddenForNormalTab() {
   window.removeEventListener("tabviewhidden", onTabViewHiddenForNormalTab, false);
   ok(!TabView.isVisible(), "Tab View is not visible");
 
   // verify that the normal tab is still selected
-  ok(gBrowser.selectedTab == originalTab, "the normal tab is still selected");
+  ok(originalTab.selected, "the normal tab is still selected");
 
   // select the app tab
   gBrowser.selectedTab = appTab;
-  ok(gBrowser.selectedTab == appTab, "the app tab is now selected");
+  ok(appTab.selected, "the app tab is now selected");
 
   // go back to tabview
   window.addEventListener("tabviewshown", onTabViewShown, false);
   TabView.toggle();
 }
 
 // ----------
 function onTabViewShown() {
@@ -72,21 +72,21 @@ function onTabViewShown() {
 }
 
 // ----------
 function onTabViewHiddenForAppTab() {
   window.removeEventListener("tabviewhidden", onTabViewHiddenForAppTab, false);
   ok(!TabView.isVisible(), "Tab View is not visible");
 
   // verify that the app tab is still selected
-  ok(gBrowser.selectedTab == appTab, "the app tab is still selected");
+  ok(appTab.selected, "the app tab is still selected");
 
   // clean up
-  gBrowser.selectedTab = originalTab; 
+  gBrowser.selectedTab = originalTab;
   gBrowser.removeTab(appTab);
 
   is(gBrowser.tabs.length, 1, "we finish with one tab");
-  ok(gBrowser.selectedTab == originalTab,
+  ok(originalTab.selected,
       "we finish with the normal tab selected");
   ok(!TabView.isVisible(), "we finish with Tab View not visible");
 
   finish();
 }
--- a/browser/components/tabview/ui.js
+++ b/browser/components/tabview/ui.js
@@ -818,17 +818,17 @@ let UI = {
       AllTabs.unregister(name, this._eventListeners[name]);
   },
 
   // ----------
   // Function: goToTab
   // Selects the given xul:tab in the browser.
   goToTab: function UI_goToTab(xulTab) {
     // If it's not focused, the onFocus listener would handle it.
-    if (gBrowser.selectedTab == xulTab)
+    if (xulTab.selected)
       this.onTabSelect(xulTab);
     else
       gBrowser.selectedTab = xulTab;
   },
 
   // ----------
   // Function: onTabSelect
   // Called when the user switches from one tab to another outside of the TabView UI.
@@ -931,17 +931,17 @@ let UI = {
       return;
 
     let tab = gBrowser.tabs[index];
 
     // When TabView is visible, we need to call onTabSelect to make sure that
     // TabView is hidden and that the correct group is activated. When a modal
     // dialog is shown for currently selected tab the onTabSelect event handler
     // is not called, so we need to do it.
-    if (gBrowser.selectedTab == tab && this._currentTab == tab)
+    if (tab.selected && this._currentTab == tab)
       this.onTabSelect(tab);
   },
 
   // ----------
   // Function: setReorderTabsOnHide
   // Sets the groupItem which the tab items' tabs should be re-ordered when
   // switching to the main browser UI.
   // Parameters:
deleted file mode 100644
--- a/browser/config/mozconfigs/macosx32/debug
+++ /dev/null
@@ -1,16 +0,0 @@
-. $topsrcdir/build/macosx/mozconfig.leopard
-
-ac_add_options --enable-debug
-ac_add_options --enable-trace-malloc
-ac_add_options --enable-signmar
-ENABLE_MARIONETTE=1
-
-# Needed to enable breakpad in application.ini
-export MOZILLA_OFFICIAL=1
-
-ac_add_options --with-macbundlename-prefix=Firefox
-
-# Package js shell.
-export MOZ_PACKAGE_JSSHELL=1
-
-. "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win32/debug
+++ b/browser/config/mozconfigs/win32/debug
@@ -13,12 +13,15 @@ if test -z "${_PYMAKE}"; then
 fi
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2010-win64
 else
   . $topsrcdir/build/win32/mozconfig.vs2010
 fi
 
+# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
+ac_add_options --enable-warnings-as-errors
+
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win32/nightly
+++ b/browser/config/mozconfigs/win32/nightly
@@ -22,12 +22,15 @@ if test -z "${_PYMAKE}"; then
 fi
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2010-win64
 else
   . $topsrcdir/build/win32/mozconfig.vs2010
 fi
 
+# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
+ac_add_options --enable-warnings-as-errors
+
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win32/release
+++ b/browser/config/mozconfigs/win32/release
@@ -19,12 +19,15 @@ if test -z "${_PYMAKE}"; then
 fi
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2010-win64
 else
   . $topsrcdir/build/win32/mozconfig.vs2010
 fi
 
+# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
+ac_add_options --enable-warnings-as-errors
+
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/debug
+++ b/browser/config/mozconfigs/win64/debug
@@ -8,14 +8,17 @@ ENABLE_MARIONETTE=1
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 if test -z "${_PYMAKE}"; then
   mk_add_options MOZ_MAKE_FLAGS=-j1
 fi
 
+# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
+ac_add_options --enable-warnings-as-errors
+
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 . $topsrcdir/build/win64/mozconfig.vs2010
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/nightly
+++ b/browser/config/mozconfigs/win64/nightly
@@ -17,14 +17,17 @@ ac_add_options --enable-js-diagnostics
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 if test -z "${_PYMAKE}"; then
   mk_add_options MOZ_MAKE_FLAGS=-j1
 fi
 
+# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
+ac_add_options --enable-warnings-as-errors
+
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 . $topsrcdir/build/win64/mozconfig.vs2010
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/tooltool-manifests/linux32/clang.manifest
+++ b/browser/config/tooltool-manifests/linux32/clang.manifest
@@ -1,17 +1,17 @@
 [
 {
-"clang_version": "r170377"
+"clang_version": "r170890"
 },
 {
 "size": 47,
 "digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
 "algorithm": "sha512",
 "filename": "setup.sh"
 },
 {
-"size": 61878564,
-"digest": "bc344ad6cb8f4d7b25447a8f06ae3a22c6b90283fcc70f28f12578bdaf01ec960a774cdc215bdda4960cef04b6a991e462daeeda4f7e802bf65e6c9a3967f66c",
+"size": 61878284,
+"digest": "a3f924e7a6d8651b3466f3dc4625eaa00f90ee6cc824ddd4da7a27ce26916220615c1aa33e4a286b757b9283c536b8311edddf469d23e9e1306b90dff19ae11f",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2"
 }
 ]
--- a/browser/config/tooltool-manifests/linux64/clang.manifest
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest
@@ -1,17 +1,17 @@
 [
 {
-"clang_version": "r170377"
+"clang_version": "r170890"
 },
 {
 "size": 47,
 "digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
 "algorithm": "sha512",
 "filename": "setup.sh"
 },
 {
-"size": 62277867,
-"digest": "b9c6ab4069e336fcbe705f07fd2beda37aecfd4078863898826c9591305b92f3f3f762e7f5c1b0eeb7e0fb1c0dacbca60f24868e0b3181bd34dcd9c5d44d20ee",
+"size": 62279506,
+"digest": "aa886361161a7d32aad71dfe5fd6b6cdff25610d8e32153a80caea66a8475aa8526d8529d8ac26e8ac26bd32878ee0df6d8dbef95dfd9faacec45b4ab918d52d",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2"
 }
 ]
deleted file mode 100644
--- a/browser/config/tooltool-manifests/macosx32/releng.manifest
+++ /dev/null
@@ -1,17 +0,0 @@
-[
-{
-"clang_version": "r170377"
-},
-{
-"size": 47,
-"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
-"algorithm": "sha512",
-"filename": "setup.sh"
-},
-{
-"size": 56131193,
-"digest": "a1e705d3a72e0e95eeb15722f538a3908277f9f756909ce5e67f0a09b8531c1ba7fcc4816e20795af2e98839e0308b1bc6df95308cda310ae06abf21d429624f",
-"algorithm": "sha512",
-"filename": "clang.tar.bz2"
-}
-]
--- a/browser/config/tooltool-manifests/macosx64/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,17 +1,17 @@
 [
 {
-"clang_version": "r170377"
+"clang_version": "r170890"
 },
 {
 "size": 47,
 "digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
 "algorithm": "sha512",
 "filename": "setup.sh"
 },
 {
-"size": 56131193,
-"digest": "a1e705d3a72e0e95eeb15722f538a3908277f9f756909ce5e67f0a09b8531c1ba7fcc4816e20795af2e98839e0308b1bc6df95308cda310ae06abf21d429624f",
+"size": 56126352,
+"digest": "e156e2a39abd5bf272ee30748a6825f22ddd27565b097c66662a2a6f2e9892bc5b4bf30a3552dffbe867dbfc39e7ee086e0b2cd7935f6ea216c0cf936178a88f",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2"
 }
 ]
--- a/browser/devtools/framework/ToolDefinitions.jsm
+++ b/browser/devtools/framework/ToolDefinitions.jsm
@@ -61,17 +61,17 @@ XPCOMUtils.defineLazyGetter(this, "profi
 
 // Definitions
 let webConsoleDefinition = {
   id: "webconsole",
   key: l10n("cmd.commandkey", webConsoleStrings),
   accesskey: l10n("webConsoleCmd.accesskey", webConsoleStrings),
   modifiers: Services.appinfo.OS == "Darwin" ? "accel,alt" : "accel,shift",
   ordinal: 0,
-  icon: "chrome://browser/skin/devtools/webconsole-tool-icon.png",
+  icon: "chrome://browser/skin/devtools/tool-webconsole.png",
   url: "chrome://browser/content/devtools/webconsole.xul",
   label: l10n("ToolboxWebconsole.label", webConsoleStrings),
   tooltip: l10n("ToolboxWebconsole.tooltip", webConsoleStrings),
 
   isTargetSupported: function(target) {
     return true;
   },
   build: function(iframeWindow, toolbox) {
@@ -82,17 +82,17 @@ let webConsoleDefinition = {
 
 let debuggerDefinition = {
   id: "jsdebugger",
   key: l10n("open.commandkey", debuggerStrings),
   accesskey: l10n("debuggerMenu.accesskey", debuggerStrings),
   modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
   ordinal: 1,
   killswitch: "devtools.debugger.enabled",
-  icon: "chrome://browser/skin/devtools/tools-icons-small.png",
+  icon: "chrome://browser/skin/devtools/tool-debugger.png",
   url: "chrome://browser/content/debugger.xul",
   label: l10n("ToolboxDebugger.label", debuggerStrings),
   tooltip: l10n("ToolboxDebugger.tooltip", debuggerStrings),
 
   isTargetSupported: function(target) {
     return true;
   },
 
@@ -103,17 +103,17 @@ let debuggerDefinition = {
 };
 
 let inspectorDefinition = {
   id: "inspector",
   accesskey: l10n("inspector.accesskey", inspectorStrings),
   key: l10n("inspector.commandkey", inspectorStrings),
   ordinal: 2,
   modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
-  icon: "chrome://browser/skin/devtools/tools-icons-small.png",
+  icon: "chrome://browser/skin/devtools/tool-inspector.png",
   url: "chrome://browser/content/devtools/inspector/inspector.xul",
   label: l10n("inspector.label", inspectorStrings),
   tooltip: l10n("inspector.tooltip", inspectorStrings),
 
   isTargetSupported: function(target) {
     return !target.isRemote;
   },
 
@@ -125,33 +125,33 @@ let inspectorDefinition = {
 
 let styleEditorDefinition = {
   id: "styleeditor",
   key: l10n("open.commandkey", styleEditorStrings),
   ordinal: 3,
   accesskey: l10n("open.accesskey", styleEditorStrings),
   modifiers: "shift",
   label: l10n("ToolboxStyleEditor.label", styleEditorStrings),
+  icon: "chrome://browser/skin/devtools/tool-styleeditor.png",
   url: "chrome://browser/content/styleeditor.xul",
   tooltip: l10n("ToolboxStyleEditor.tooltip", styleEditorStrings),
 
   isTargetSupported: function(target) {
     return !target.isRemote && !target.isChrome;
   },
 
   build: function(iframeWindow, toolbox) {
     let panel = new StyleEditorPanel(iframeWindow, toolbox);
     return panel.open();
   }
 };
 
 let profilerDefinition = {
   id: "jsprofiler",
   killswitch: "devtools.profiler.enabled",
-  icon: "chrome://browser/skin/devtools/tools-icons-small.png",
   url: "chrome://browser/content/profiler.xul",
   label: l10n("profiler.label", profilerStrings),
   tooltip: l10n("profiler.tooltip", profilerStrings),
 
   isTargetSupported: function (target) {
     if (target.isRemote || target.isChrome) {
       return false;
     }
--- a/browser/devtools/framework/Toolbox.jsm
+++ b/browser/devtools/framework/Toolbox.jsm
@@ -357,16 +357,19 @@ Toolbox.prototype = {
     let id = toolDefinition.id;
 
     let radio = this.doc.createElement("radio");
     radio.setAttribute("label", toolDefinition.label);
     radio.className = "toolbox-tab devtools-tab";
     radio.id = "toolbox-tab-" + id;
     radio.setAttribute("toolid", id);
     radio.setAttribute("tooltiptext", toolDefinition.tooltip);
+    if (toolDefinition.icon) {
+      radio.setAttribute("src", toolDefinition.icon);
+    }
 
     let ordinal = (typeof toolDefinition.ordinal == "number") ?
                   toolDefinition.ordinal : MAX_ORDINAL;
     radio.setAttribute("ordinal", ordinal);
 
     radio.addEventListener("command", function(id) {
       this.selectTool(id);
     }.bind(this, id));
--- a/browser/devtools/framework/connect/connect.css
+++ b/browser/devtools/framework/connect/connect.css
@@ -73,22 +73,22 @@ body.connecting > #connecting {
 #connecting {
   text-align: center;
 }
 
 #connecting > p > img {
   vertical-align: top;
 }
 
-#actors {
+.actors {
   padding-left: 0;
   font-size: 0.9rem;
 }
 
-#actors > a {
+.actors > a {
   display: block;
   margin: 5px;
   padding: 5px;
   color: white;
 }
 
 .remote-process {
   font-style: italic;
--- a/browser/devtools/framework/connect/connect.js
+++ b/browser/devtools/framework/connect/connect.js
@@ -66,41 +66,43 @@ function submit() {
  * Connection is ready. List actors and build buttons.
  */
 function onConnectionReady(aType, aTraits) {
   clearTimeout(gConnectionTimeout);
   gClient.listTabs(function(aResponse) {
     document.body.classList.remove("connecting");
     document.body.classList.add("actors-mode");
 
-    let parent = document.getElementById("actors");
+    let parent = document.getElementById("tabActors");
 
     // Add Global Process debugging...
     let globals = JSON.parse(JSON.stringify(aResponse));
     delete globals.tabs;
     delete globals.selected;
     // ...only if there are appropriate actors (a 'from' property will always
     // be there).
 
     // Add one entry for each open tab.
     for (let i = 0; i < aResponse.tabs.length; i++) {
       buildLink(aResponse.tabs[i], parent, i == aResponse.selected);
     }
 
+    let gParent = document.getElementById("globalActors");
+
     // Build the Remote Process button
     if (Object.keys(globals).length > 1) {
       let a = document.createElement("a");
       a.onclick = function() {
         openToolbox(globals, true);
 
       }
-      a.title = a.textContent = window.l10n.GetStringFromName("remoteProcess");
+      a.title = a.textContent = window.l10n.GetStringFromName("mainProcess");
       a.className = "remote-process";
       a.href = "#";
-      parent.appendChild(a);
+      gParent.appendChild(a);
     }
     // Move the selected tab on top
     let selectedLink = parent.querySelector("a.selected");
     if (selectedLink) {
       parent.insertBefore(selectedLink, parent.firstChild);
     }
 
     // Ensure the first link is focused
@@ -159,9 +161,10 @@ function handleConnectionTimeout() {
 
 /**
  * The user clicked on one of the buttons.
  * Opens the toolbox.
  */
 function openToolbox(form, chrome=false) {
   let target = TargetFactory.forRemote(form, gClient, chrome);
   gDevTools.showToolbox(target, "webconsole", Toolbox.HostType.WINDOW);
+  window.close();
 }
--- a/browser/devtools/framework/connect/connect.xhtml
+++ b/browser/devtools/framework/connect/connect.xhtml
@@ -32,17 +32,19 @@
           <input class="devtools-toolbarbutton" id="submit" type="submit" value="&connect;"></input>
         </label>
       </form>
       <p class="error-message error-timeout">&errorTimeout;</p>
       <p class="error-message error-refused">&errorRefused;</p>
       <p class="error-message error-unexpected">&errorUnexpected;</p>
     </section>
     <section id="actors-list">
-      <p>&availability;</p>
-      <ul id="actors"></ul>
+      <p>&availableTabs;</p>
+      <ul class="actors" id="tabActors"></ul>
+      <p>&availableProcesses;</p>
+      <ul class="actors" id="globalActors"></ul>
     </section>
     <section id="connecting">
       <p><img src="chrome://browser/skin/tabbrowser/loading.png"></img> &connecting;</p>
     </section>
     <footer>&help;</footer>
   </body>
 </html>
--- a/browser/devtools/framework/toolbox.css
+++ b/browser/devtools/framework/toolbox.css
@@ -3,8 +3,13 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 .devtools-tab > .radio-check,
 .devtools-tab > .radio-check-box1,
 .devtools-tab > .radio-spacer-box {
   display: none;
 }
 
+#toolbox-controls > toolbarbutton > .toolbarbutton-text,
+#toolbox-dock-buttons > toolbarbutton > .toolbarbutton-text,
+.command-button > .toolbarbutton-text {
+  display: none;
+}
--- a/browser/devtools/framework/toolbox.xul
+++ b/browser/devtools/framework/toolbox.xul
@@ -2,42 +2,40 @@
 <!-- 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/. -->
 <!DOCTYPE window [
 <!ENTITY % toolboxDTD SYSTEM "chrome://browser/locale/devtools/toolbox.dtd" >
  %toolboxDTD;
 ]>
 <?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/content/devtools/shared/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/devtools/framework/toolbox.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/devtools/toolbox.css" type="text/css"?>
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/source-editor-overlay.xul"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <notificationbox id="toolbox-notificationbox" flex="1">
     <toolbar class="devtools-tabbar">
 #ifdef XP_MACOSX
       <hbox id="toolbox-controls">
         <toolbarbutton id="toolbox-close"
-                       class="devtools-closebutton"
                        tooltiptext="&toolboxCloseButton.tooltip;"/>
         <hbox id="toolbox-dock-buttons"/>
       </hbox>
 #endif
       <radiogroup id="toolbox-tabs" orient="horizontal">
       </radiogroup>
       <hbox id="toolbox-buttons" flex="1" pack="end"/>
 #ifndef XP_MACOSX
+      <vbox id="toolbox-controls-separator"/>
       <hbox id="toolbox-controls">
         <hbox id="toolbox-dock-buttons"/>
         <toolbarbutton id="toolbox-close"
-                       class="devtools-closebutton"
                        tooltiptext="&toolboxCloseButton.tooltip;"/>
       </hbox>
 #endif
     </toolbar>
     <deck id="toolbox-deck" flex="1">
     </deck>
   </notificationbox>
 </window>
--- a/browser/devtools/inspector/InspectorPanel.jsm
+++ b/browser/devtools/inspector/InspectorPanel.jsm
@@ -196,23 +196,31 @@ InspectorPanel.prototype = {
   /**
    * Reset the inspector on navigate away.
    */
   onNavigatedAway: function InspectorPanel_onNavigatedAway(event, newWindow) {
     this.selection.setNode(null);
     this._destroyMarkup();
     this.isDirty = false;
     let self = this;
-    newWindow.addEventListener("DOMContentLoaded", function onDOMReady() {
-      newWindow.removeEventListener("DOMContentLoaded", onDOMReady, true);;
+
+    function onDOMReady() {
+      newWindow.removeEventListener("DOMContentLoaded", onDOMReady, true);
+
       if (!self.selection.node) {
         self.selection.setNode(newWindow.document.documentElement);
       }
       self._initMarkup();
-    }, true);
+    }
+
+    if (newWindow.document.readyState == "loading") {
+      newWindow.addEventListener("DOMContentLoaded", onDOMReady, true);
+    } else {
+      onDOMReady();
+    }
   },
 
   /**
    * Show a message if the inspector is dirty.
    */
   preventNavigateAway: function InspectorPanel_preventNavigateAway(event, request) {
     if (!this.isDirty) {
       return;
--- a/browser/devtools/responsivedesign/CmdResize.jsm
+++ b/browser/devtools/responsivedesign/CmdResize.jsm
@@ -26,17 +26,17 @@ gcli.addCommand({
   description: gcli.lookup('resizeModeOffDesc'),
   manual: gcli.lookup('resizeModeManual'),
   exec: gcli_cmd_resize
 });
 
 gcli.addCommand({
   name: 'resize toggle',
   buttonId: "command-button-responsive",
-  buttonClass: "command-button devtools-toolbarbutton",
+  buttonClass: "command-button",
   tooltipText: gcli.lookup("resizeModeToggleTooltip"),
   description: gcli.lookup('resizeModeToggleDesc'),
   manual: gcli.lookup('resizeModeManual'),
   exec: gcli_cmd_resize
 });
 
 gcli.addCommand({
   name: 'resize to',
--- a/browser/devtools/scratchpad/CmdScratchpad.jsm
+++ b/browser/devtools/scratchpad/CmdScratchpad.jsm
@@ -7,16 +7,16 @@ this.EXPORTED_SYMBOLS = [ ];
 Components.utils.import("resource:///modules/devtools/gcli.jsm");
 
 /**
  * 'scratchpad' command
  */
 gcli.addCommand({
   name: "scratchpad",
   buttonId: "command-button-scratchpad",
-  buttonClass: "command-button devtools-toolbarbutton",
+  buttonClass: "command-button",
   tooltipText: gcli.lookup("scratchpadOpenTooltip"),
   hidden: true,
   exec: function(args, context) {
     let chromeWindow = context.environment.chromeDocument.defaultView;
     chromeWindow.Scratchpad.ScratchpadManager.openScratchpad();
   }
 });
--- a/browser/devtools/styleinspector/CssHtmlTree.jsm
+++ b/browser/devtools/styleinspector/CssHtmlTree.jsm
@@ -1199,23 +1199,23 @@ SelectorView.prototype = {
       return this.text(aElement) + " \u2192 " + this.selectorInfo.value;
     }
   },
 
   text: function SelectorView_text(aElement) {
     let result = this.selectorInfo.selector.text;
     if (this.selectorInfo.elementStyle) {
       let source = this.selectorInfo.sourceElement;
-      let IUI = this.tree.styleInspector.IUI;
-      if (IUI && IUI.selection == source) {
+      let inspector = this.tree.styleInspector.inspector;
+
+      if (inspector.selection.node == source) {
         result = "this";
       } else {
         result = CssLogic.getShortName(source);
       }
-
       result += ".style";
     }
 
     return result;
   },
 
   maybeOpenStyleEditor: function(aEvent)
   {
--- a/browser/devtools/tilt/CmdTilt.jsm
+++ b/browser/devtools/tilt/CmdTilt.jsm
@@ -38,17 +38,17 @@ gcli.addCommand({
 
 
 /**
  * 'tilt toggle' command
  */
 gcli.addCommand({
   name: "tilt toggle",
   buttonId: "command-button-tilt",
-  buttonClass: "command-button  devtools-toolbarbutton",
+  buttonClass: "command-button",
   tooltipText: gcli.lookup("tiltToggleTooltip"),
   hidden: true,
   exec: function(args, context) {
     let chromeWindow = context.environment.chromeDocument.defaultView;
     let Tilt = TiltManager.getTiltForBrowser(chromeWindow);
     Tilt.toggle();
   }
 });
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,4 +1,4 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 0.6.172
+Current extension version is: 0.7.28
 
--- a/browser/extensions/pdfjs/components/PdfStreamConverter.js
+++ b/browser/extensions/pdfjs/components/PdfStreamConverter.js
@@ -25,57 +25,53 @@ const Cr = Components.results;
 const Cu = Components.utils;
 // True only if this is the version of pdf.js that is included with firefox.
 const MOZ_CENTRAL = true;
 const PDFJS_EVENT_ID = 'pdf.js.message';
 const PDF_CONTENT_TYPE = 'application/pdf';
 const PREF_PREFIX = 'pdfjs';
 const PDF_VIEWER_WEB_PAGE = 'resource://pdf.js/web/viewer.html';
 const MAX_DATABASE_LENGTH = 4096;
-const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://gre/modules/NetUtil.jsm');
 
+XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils',
+  'resource://gre/modules/PrivateBrowsingUtils.jsm');
 
-let appInfo = Cc['@mozilla.org/xre/app-info;1']
-                  .getService(Ci.nsIXULAppInfo);
 let Svc = {};
 XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
                                    '@mozilla.org/mime;1',
                                    'nsIMIMEService');
 
-let isInPrivateBrowsing;
-if (appInfo.ID === FIREFOX_ID) {
-  let privateBrowsing = Cc['@mozilla.org/privatebrowsing;1']
-                            .getService(Ci.nsIPrivateBrowsingService);
-  isInPrivateBrowsing = function getInPrivateBrowsing() {
-    return privateBrowsing.privateBrowsingEnabled;
-  };
-} else {
-  isInPrivateBrowsing = function() { return false; };
-}
-
 function getChromeWindow(domWindow) {
   var containingBrowser = domWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                                    .getInterface(Ci.nsIWebNavigation)
                                    .QueryInterface(Ci.nsIDocShell)
                                    .chromeEventHandler;
   return containingBrowser.ownerDocument.defaultView;
 }
 
 function getBoolPref(pref, def) {
   try {
     return Services.prefs.getBoolPref(pref);
   } catch (ex) {
     return def;
   }
 }
 
+function getIntPref(pref, def) {
+  try {
+    return Services.prefs.getIntPref(pref);
+  } catch (ex) {
+    return def;
+  }
+}
+
 function setStringPref(pref, value) {
   let str = Cc['@mozilla.org/supports-string;1']
               .createInstance(Ci.nsISupportsString);
   str.data = value;
   Services.prefs.setComplexValue(pref, Ci.nsISupportsString, str);
 }
 
 function getStringPref(pref, def) {
@@ -206,38 +202,53 @@ PdfDataListener.prototype = {
 
 // All the priviledged actions.
 function ChromeActions(domWindow, dataListener) {
   this.domWindow = domWindow;
   this.dataListener = dataListener;
 }
 
 ChromeActions.prototype = {
+  isInPrivateBrowsing: function() {
+    let docIsPrivate;
+    try {
+      docIsPrivate = PrivateBrowsingUtils.isWindowPrivate(this.domWindow);
+    } catch (x) {
+      // unable to use PrivateBrowsingUtils, e.g. FF15
+    }
+    if (typeof docIsPrivate === 'undefined') {
+      // per-window Private Browsing is not supported, trying global service
+      try {
+        let privateBrowsing = Cc['@mozilla.org/privatebrowsing;1']
+                                  .getService(Ci.nsIPrivateBrowsingService);
+        docIsPrivate = privateBrowsing.privateBrowsingEnabled;
+      } catch (x) {
+        // unable to get nsIPrivateBrowsingService (e.g. not Firefox)
+        docIsPrivate = false;
+      }
+    }
+    // caching the result
+    this.isInPrivateBrowsing = function isInPrivateBrowsingCached() {
+      return docIsPrivate;
+    };
+    return docIsPrivate;
+  },
   download: function(data, sendResponse) {
     var originalUrl = data.originalUrl;
     // The data may not be downloaded so we need just retry getting the pdf with
     // the original url.
     var originalUri = NetUtil.newURI(data.originalUrl);
     var blobUri = data.blobUrl ? NetUtil.newURI(data.blobUrl) : originalUri;
     var extHelperAppSvc =
           Cc['@mozilla.org/uriloader/external-helper-app-service;1'].
              getService(Ci.nsIExternalHelperAppService);
     var frontWindow = Cc['@mozilla.org/embedcomp/window-watcher;1'].
                          getService(Ci.nsIWindowWatcher).activeWindow;
 
-    let docIsPrivate = false;
-    try {
-      docIsPrivate = this.domWindow
-                         .QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsILoadContext)
-                         .usePrivateBrowsing;
-    } catch (x) {
-    }
-
+    let docIsPrivate = this.isInPrivateBrowsing();
     let netChannel = NetUtil.newChannel(blobUri);
     if ('nsIPrivateBrowsingChannel' in Ci &&
         netChannel instanceof Ci.nsIPrivateBrowsingChannel) {
       netChannel.setPrivate(docIsPrivate);
     }
     NetUtil.asyncFetch(netChannel, function(aInputStream, aResult) {
       if (!Components.isSuccessCode(aResult)) {
         if (sendResponse)
@@ -276,25 +287,25 @@ ChromeActions.prototype = {
                                            aOffset, aCount);
         }
       };
 
       channel.asyncOpen(listener, null);
     });
   },
   setDatabase: function(data) {
-    if (isInPrivateBrowsing())
+    if (this.isInPrivateBrowsing())
       return;
     // Protect against something sending tons of data to setDatabase.
     if (data.length > MAX_DATABASE_LENGTH)
       return;
     setStringPref(PREF_PREFIX + '.database', data);
   },
   getDatabase: function() {
-    if (isInPrivateBrowsing())
+    if (this.isInPrivateBrowsing())
       return '{}';
     return getStringPref(PREF_PREFIX + '.database', '{}');
   },
   getLocale: function() {
     return getStringPref('general.useragent.locale', 'en-US');
   },
   getLoadingType: function() {
     return this.dataListener ? 'passive' : 'active';
@@ -346,16 +357,20 @@ ChromeActions.prototype = {
   },
   supportsIntegratedFind: function() {
     // Integrated find is only supported when we're not in a frame and when the
     // new find events code exists.
     return this.domWindow.frameElement === null &&
            getChromeWindow(this.domWindow).gFindBar &&
            'updateControlState' in getChromeWindow(this.domWindow).gFindBar;
   },
+  supportsDocumentFonts: function() {
+    var pref = getIntPref('browser.display.use_document_fonts', 1);
+    return !!pref;
+  },
   fallback: function(url, sendResponse) {
     var self = this;
     var domWindow = this.domWindow;
     var strings = getLocalizedStrings('chrome.properties');
     var message = getLocalizedString(strings, 'unsupported_feature');
 
     var notificationBox = null;
     // Multiple browser windows can be opened, finding one for notification box
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -151,16 +151,23 @@ let PdfJs = {
     if (stringTypes !== '') {
       types = stringTypes.split(',');
     }
 
     if (types.indexOf(PDF_CONTENT_TYPE) === -1) {
       types.push(PDF_CONTENT_TYPE);
     }
     prefs.setCharPref(PREF_DISABLED_PLUGIN_TYPES, types.join(','));
+
+    // Update the category manager in case the plugins are already loaded.
+    let categoryManager = Cc["@mozilla.org/categorymanager;1"];
+    categoryManager.getService(Ci.nsICategoryManager).
+                    deleteCategoryEntry("Gecko-Content-Viewers",
+                                        PDF_CONTENT_TYPE,
+                                        false);
   },
 
   // nsIObserver
   observe: function observe(aSubject, aTopic, aData) {
     if (this.enabled)
       this._ensureRegistered();
     else
       this._ensureUnregistered();
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -17,17 +17,17 @@
 
 var PDFJS = {};
 
 (function pdfjsWrapper() {
   // Use strict in our context only - users might not want it
   'use strict';
 
   PDFJS.build =
-'3c7ef79';
+'e22ee54';
 
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* Copyright 2012 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -210,17 +210,19 @@ var Page = (function PageClosure() {
     getOperatorList: function Page_getOperatorList(handler, dependency) {
       var xref = this.xref;
       var contentStream = this.getContentStream();
       var resources = this.resources;
       var pe = this.pe = new PartialEvaluator(
                                 xref, handler, this.pageIndex,
                                 'p' + this.pageIndex + '_');
 
-      return pe.getOperatorList(contentStream, resources, dependency);
+      var list = pe.getOperatorList(contentStream, resources, dependency);
+      pe.optimizeQueue(list);
+      return list;
     },
     extractTextContent: function Page_extractTextContent() {
       var handler = {
         on: function nullHandlerOn() {},
         send: function nullHandlerSend() {}
       };
 
       var xref = this.xref;
@@ -301,16 +303,22 @@ var Page = (function PageClosure() {
                     url = '';
                   item.url = url;
                   break;
                 case 'GoTo':
                   item.dest = a.get('D');
                   break;
                 case 'GoToR':
                   var url = a.get('F');
+                  if (isDict(url)) {
+                    // We assume that the 'url' is a Filspec dictionary
+                    // and fetch the url without checking any further
+                    url = url.get('F') || '';
+                  }
+
                   // TODO: pdf reference says that GoToR
                   // can also have 'NewWindow' attribute
                   if (!isValidUrl(url))
                     url = '';
                   item.url = url;
                   item.dest = a.get('D');
                   break;
                 default:
@@ -1354,16 +1362,23 @@ var PDFDocumentProxy = (function PDFDocu
     /**
      * @return {string} A unique ID to identify a PDF. Not guaranteed to be
      * unique.
      */
     get fingerprint() {
       return this.pdfInfo.fingerprint;
     },
     /**
+     * @return {boolean} true if embedded document fonts are in use. Will be
+     * set during rendering of the pages.
+     */
+    get embeddedFontsUsed() {
+      return this.transport.embeddedFontsUsed;
+    },
+    /**
      * @param {number} The page number to get. The first page is 1.
      * @return {Promise} A promise that is resolved with a {PDFPageProxy}
      * object.
      */
     getPage: function PDFDocumentProxy_getPage(number) {
       return this.transport.getPage(number);
     },
     /**
@@ -1600,16 +1615,19 @@ var PDFPageProxy = (function PDFPageProx
       // Convert the font names to the corresponding font obj.
       var fontObjs = [];
       for (var i = 0, ii = fonts.length; i < ii; i++) {
         var obj = this.commonObjs.getData(fonts[i]);
         if (obj.error) {
           warn('Error during font loading: ' + obj.error);
           continue;
         }
+        if (!obj.coded) {
+          this.transport.embeddedFontsUsed = true;
+        }
         fontObjs.push(obj);
       }
 
       // Load all the fonts
       FontLoader.bind(
         fontObjs,
         function pageEnsureFontsFontObjs(fontObjs) {
           this.stats.timeEnd('Font Loading');
@@ -1705,17 +1723,17 @@ var PDFPageProxy = (function PDFPageProx
  */
 var WorkerTransport = (function WorkerTransportClosure() {
   function WorkerTransport(workerInitializedPromise, workerReadyPromise) {
     this.workerReadyPromise = workerReadyPromise;
     this.commonObjs = new PDFObjects();
 
     this.pageCache = [];
     this.pagePromises = [];
-    this.fontsLoading = {};
+    this.embeddedFontsUsed = false;
 
     // If worker support isn't disabled explicit and the browser has worker
     // support, create a new web worker and test if it/the browser fullfills
     // all requirements to run parts of pdf.js in a web worker.
     // Right now, the requirement is, that an Uint8Array is still an Uint8Array
     // as it arrives on the worker. Chrome added this with version 15.
     if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') {
       var workerSrc = PDFJS.workerSrc;
@@ -2171,16 +2189,91 @@ var CanvasGraphics = (function CanvasGra
     this.commonObjs = commonObjs;
     this.objs = objs;
     this.textLayer = textLayer;
     if (canvasCtx) {
       addContextCurrentTransform(canvasCtx);
     }
   }
 
+  function applyStencilMask(imgArray, width, height, inverseDecode, buffer) {
+    var imgArrayPos = 0;
+    var i, j, mask, buf;
+    // removing making non-masked pixels transparent
+    var bufferPos = 3; // alpha component offset
+    for (i = 0; i < height; i++) {
+      mask = 0;
+      for (j = 0; j < width; j++) {
+        if (!mask) {
+          buf = imgArray[imgArrayPos++];
+          mask = 128;
+        }
+        if (!(buf & mask) == inverseDecode) {
+          buffer[bufferPos] = 0;
+        }
+        bufferPos += 4;
+        mask >>= 1;
+      }
+    }
+  }
+
+  function rescaleImage(pixels, width, height, widthScale, heightScale) {
+    var scaledWidth = Math.ceil(width / widthScale);
+    var scaledHeight = Math.ceil(height / heightScale);
+
+    var itemsSum = new Float32Array(scaledWidth * scaledHeight * 3);
+    var itemsCount = new Float32Array(scaledWidth * scaledHeight);
+    var maxAlphas = new Uint8Array(scaledWidth * scaledHeight);
+    for (var i = 0, position = 0; i < height; i++) {
+      var lineOffset = (0 | (i / heightScale)) * scaledWidth;
+      for (var j = 0; j < width; j++) {
+        var countOffset = lineOffset + (0 | (j / widthScale));
+        var sumOffset = countOffset * 3;
+        var maxAlpha = maxAlphas[countOffset];
+        var currentAlpha = pixels[position + 3];
+        if (maxAlpha < currentAlpha) {
+          // lowering total alpha
+          var scale = 1 - (currentAlpha - maxAlpha) / 255;
+          itemsSum[sumOffset] *= scale;
+          itemsSum[sumOffset + 1] *= scale;
+          itemsSum[sumOffset + 2] *= scale;
+          maxAlphas[countOffset] = maxAlpha = currentAlpha;
+        }
+        if (maxAlpha > currentAlpha) {
+          var scale = 1 - (maxAlpha - currentAlpha) / 255;
+          itemsSum[sumOffset] += pixels[position] * scale;
+          itemsSum[sumOffset + 1] += pixels[position + 1] * scale;
+          itemsSum[sumOffset + 2] += pixels[position + 2] * scale;
+          itemsCount[countOffset] += scale;
+        } else {
+          itemsSum[sumOffset] += pixels[position];
+          itemsSum[sumOffset + 1] += pixels[position + 1];
+          itemsSum[sumOffset + 2] += pixels[position + 2];
+          itemsCount[countOffset]++;
+        }
+        position += 4;
+      }
+    }
+    var tmpCanvas = createScratchCanvas(scaledWidth, scaledHeight);
+    var tmpCtx = tmpCanvas.getContext('2d');
+    var imgData = tmpCtx.getImageData(0, 0, scaledWidth, scaledHeight);
+    pixels = imgData.data;
+    var j = 0, q = 0;
+    for (var i = 0, ii = scaledWidth * scaledHeight; i < ii; i++) {
+      var count = itemsCount[i];
+      pixels[j] = itemsSum[q++] / count;
+      pixels[j + 1] = itemsSum[q++] / count;
+      pixels[j + 2] = itemsSum[q++] / count;
+      pixels[j + 3] = maxAlphas[i];
+      j += 4;
+    }
+    tmpCtx.putImageData(imgData, 0, 0);
+    return tmpCanvas;
+  }
+
   var LINE_CAP_STYLES = ['butt', 'round', 'square'];
   var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
   var NORMAL_CLIP = {};
   var EO_CLIP = {};
 
   CanvasGraphics.prototype = {
     slowCommands: {
       'stroke': true,
@@ -2202,17 +2295,20 @@ var CanvasGraphics = (function CanvasGra
       'setStrokeGray': true,
       'setFillGray': true,
       'setStrokeRGBColor': true,
       'setFillRGBColor': true,
       'setStrokeCMYKColor': true,
       'setFillCMYKColor': true,
       'paintJpegXObject': true,
       'paintImageXObject': true,
+      'paintInlineImageXObject': true,
+      'paintInlineImageXObjectGroup': true,
       'paintImageMaskXObject': true,
+      'paintImageMaskXObjectGroup': true,
       'shadingFill': true
     },
 
     beginDrawing: function CanvasGraphics_beginDrawing(viewport) {
       var transform = viewport.transform;
       this.ctx.save();
       this.ctx.transform.apply(this.ctx, transform);
 
@@ -2308,20 +2404,24 @@ var CanvasGraphics = (function CanvasGra
     },
     setLineJoin: function CanvasGraphics_setLineJoin(style) {
       this.ctx.lineJoin = LINE_JOIN_STYLES[style];
     },
     setMiterLimit: function CanvasGraphics_setMiterLimit(limit) {
       this.ctx.miterLimit = limit;
     },
     setDash: function CanvasGraphics_setDash(dashArray, dashPhase) {
-      this.ctx.mozDash = dashArray;
-      this.ctx.mozDashOffset = dashPhase;
-      this.ctx.webkitLineDash = dashArray;
-      this.ctx.webkitLineDashOffset = dashPhase;
+      var ctx = this.ctx;
+      if ('setLineDash' in ctx) {
+        ctx.setLineDash(dashArray);
+        ctx.lineDashOffset = dashPhase;
+      } else {
+        ctx.mozDash = dashArray;
+        ctx.mozDashOffset = dashPhase;
+      }
     },
     setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) {
       // Maybe if we one day fully support color spaces this will be important
       // for now we can ignore.
       // TODO set rendering intent?
     },
     setFlatness: function CanvasGraphics_setFlatness(flatness) {
       // There's no way to control this with canvas, but we can safely ignore.
@@ -3102,130 +3202,136 @@ var CanvasGraphics = (function CanvasGra
       ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height,
                     0, -h, w, h);
 
       this.restore();
     },
 
     paintImageMaskXObject: function CanvasGraphics_paintImageMaskXObject(
                              imgArray, inverseDecode, width, height) {
-      function applyStencilMask(buffer, inverseDecode) {
-        var imgArrayPos = 0;
-        var i, j, mask, buf;
-        // removing making non-masked pixels transparent
-        var bufferPos = 3; // alpha component offset
-        for (i = 0; i < height; i++) {
-          mask = 0;
-          for (j = 0; j < width; j++) {
-            if (!mask) {
-              buf = imgArray[imgArrayPos++];
-              mask = 128;
-            }
-            if (!(buf & mask) == inverseDecode) {
-              buffer[bufferPos] = 0;
-            }
-            bufferPos += 4;
-            mask >>= 1;
-          }
-        }
-      }
-      function rescaleImage(pixels, widthScale, heightScale) {
-        var scaledWidth = Math.ceil(width / widthScale);
-        var scaledHeight = Math.ceil(height / heightScale);
-
-        var itemsSum = new Uint32Array(scaledWidth * scaledHeight * 4);
-        var itemsCount = new Uint32Array(scaledWidth * scaledHeight);
-        for (var i = 0, position = 0; i < height; i++) {
-          var lineOffset = (0 | (i / heightScale)) * scaledWidth;
-          for (var j = 0; j < width; j++) {
-            var countOffset = lineOffset + (0 | (j / widthScale));
-            var sumOffset = countOffset << 2;
-            itemsSum[sumOffset] += pixels[position];
-            itemsSum[sumOffset + 1] += pixels[position + 1];
-            itemsSum[sumOffset + 2] += pixels[position + 2];
-            itemsSum[sumOffset + 3] += pixels[position + 3];
-            itemsCount[countOffset]++;
-            position += 4;
-          }
-        }
-        var tmpCanvas = createScratchCanvas(scaledWidth, scaledHeight);
-        var tmpCtx = tmpCanvas.getContext('2d');
-        var imgData = tmpCtx.getImageData(0, 0, scaledWidth, scaledHeight);
-        pixels = imgData.data;
-        for (var i = 0, j = 0, ii = scaledWidth * scaledHeight; i < ii; i++) {
-          var count = itemsCount[i];
-          pixels[j] = itemsSum[j] / count;
-          pixels[j + 1] = itemsSum[j + 1] / count;
-          pixels[j + 2] = itemsSum[j + 2] / count;
-          pixels[j + 3] = itemsSum[j + 3] / count;
-          j += 4;
-        }
-        tmpCtx.putImageData(imgData, 0, 0);
-        return tmpCanvas;
-      }
-
-      this.save();
-
       var ctx = this.ctx;
-      var w = width, h = height;
-      // scale the image to the unit square
-      ctx.scale(1 / w, -1 / h);
-
-      var tmpCanvas = createScratchCanvas(w, h);
+      var tmpCanvas = createScratchCanvas(width, height);
       var tmpCtx = tmpCanvas.getContext('2d');
 
       var fillColor = this.current.fillColor;
       tmpCtx.fillStyle = (fillColor && fillColor.hasOwnProperty('type') &&
                           fillColor.type === 'Pattern') ?
                           fillColor.getPattern(tmpCtx) : fillColor;
-      tmpCtx.fillRect(0, 0, w, h);
-
-      var imgData = tmpCtx.getImageData(0, 0, w, h);
+      tmpCtx.fillRect(0, 0, width, height);
+
+      var imgData = tmpCtx.getImageData(0, 0, width, height);
       var pixels = imgData.data;
 
-      applyStencilMask(pixels, inverseDecode);
-
-      var currentTransform = ctx.mozCurrentTransformInverse;
-      var widthScale = Math.max(Math.abs(currentTransform[0]), 1);
-      var heightScale = Math.max(Math.abs(currentTransform[3]), 1);
-      if (widthScale >= 2 || heightScale >= 2) {
-        // canvas does not resize well large images to small -- using simple
-        // algorithm to perform pre-scaling
-        tmpCanvas = rescaleImage(imgData.data, widthScale, heightScale);
-        ctx.scale(widthScale, heightScale);
-        ctx.drawImage(tmpCanvas, 0, -h / heightScale);
-      } else {
+      applyStencilMask(imgArray, width, height, inverseDecode, pixels);
+
+      this.paintInlineImageXObject(imgData);
+    },
+
+    paintImageMaskXObjectGroup:
+      function CanvasGraphics_paintImageMaskXObjectGroup(images) {
+      var ctx = this.ctx;
+      var tmpCanvasWidth = 0, tmpCanvasHeight = 0, tmpCanvas, tmpCtx;
+      for (var i = 0, ii = images.length; i < ii; i++) {
+        var image = images[i];
+        var w = image.width, h = image.height;
+        if (w > tmpCanvasWidth || h > tmpCanvasHeight) {
+          tmpCanvasWidth = Math.max(w, tmpCanvasWidth);
+          tmpCanvasHeight = Math.max(h, tmpCanvasHeight);
+          tmpCanvas = createScratchCanvas(tmpCanvasWidth, tmpCanvasHeight);
+          tmpCtx = tmpCanvas.getContext('2d');
+
+          var fillColor = this.current.fillColor;
+          tmpCtx.fillStyle = (fillColor && fillColor.hasOwnProperty('type') &&
+                              fillColor.type === 'Pattern') ?
+                              fillColor.getPattern(tmpCtx) : fillColor;
+        }
+        tmpCtx.fillRect(0, 0, w, h);
+
+        var imgData = tmpCtx.getImageData(0, 0, w, h);
+        var pixels = imgData.data;
+
+        applyStencilMask(image.data, w, h, image.inverseDecode, pixels);
+
         tmpCtx.putImageData(imgData, 0, 0);
-        ctx.drawImage(tmpCanvas, 0, -h);
-      }
-      this.restore();
+
+        ctx.save();
+        ctx.transform.apply(ctx, image.transform);
+        ctx.scale(1, -1);
+        ctx.drawImage(tmpCanvas, 0, 0, w, h,
+                      0, -1, 1, 1);
+        ctx.restore();
+      }
     },
 
     paintImageXObject: function CanvasGraphics_paintImageXObject(objId) {
       var imgData = this.objs.get(objId);
       if (!imgData)
         error('Dependent image isn\'t ready yet');
 
+      this.paintInlineImageXObject(imgData);
+    },
+
+    paintInlineImageXObject:
+      function CanvasGraphics_paintInlineImageXObject(imgData) {
+      var width = imgData.width;
+      var height = imgData.height;
+      var ctx = this.ctx;
       this.save();
+      // scale the image to the unit square
+      ctx.scale(1 / width, -1 / height);
+
+      var currentTransform = ctx.mozCurrentTransformInverse;
+      var widthScale = Math.max(Math.abs(currentTransform[0]), 1);
+      var heightScale = Math.max(Math.abs(currentTransform[3]), 1);
+      var tmpCanvas = createScratchCanvas(width, height);
+      var tmpCtx = tmpCanvas.getContext('2d');
+
+      if (widthScale >= 2 || heightScale >= 2) {
+        // canvas does not resize well large images to small -- using simple
+        // algorithm to perform pre-scaling
+        tmpCanvas = rescaleImage(imgData.data,
+                                 width, height,
+                                 widthScale, heightScale);
+        ctx.scale(widthScale, heightScale);
+        ctx.drawImage(tmpCanvas, 0, -height / heightScale);
+      } else {
+        if (typeof ImageData !== 'undefined' && imgData instanceof ImageData) {
+          tmpCtx.putImageData(imgData, 0, 0);
+        } else {
+          this.putBinaryImageData(tmpCtx, imgData);
+        }
+        ctx.drawImage(tmpCanvas, 0, -height);
+      }
+      this.restore();
+    },
+
+    paintInlineImageXObjectGroup:
+      function CanvasGraphics_paintInlineImageXObjectGroup(imgData, map) {
       var ctx = this.ctx;
       var w = imgData.width;
       var h = imgData.height;
-      // scale the image to the unit square
-      ctx.scale(1 / w, -1 / h);
 
       var tmpCanvas = createScratchCanvas(w, h);
       var tmpCtx = tmpCanvas.getContext('2d');
-      this.putBinaryImageData(tmpCtx, imgData, w, h);
-
-      ctx.drawImage(tmpCanvas, 0, -h);
-      this.restore();
-    },
-
-    putBinaryImageData: function CanvasGraphics_putBinaryImageData(ctx, imgData,
-                                                                   w, h) {
+      this.putBinaryImageData(tmpCtx, imgData);
+
+      for (var i = 0, ii = map.length; i < ii; i++) {
+        var entry = map[i];
+        ctx.save();
+        ctx.transform.apply(ctx, entry.transform);
+        ctx.scale(1, -1);
+        ctx.drawImage(tmpCanvas, entry.x, entry.y, entry.w, entry.h,
+                      0, -1, 1, 1);
+        ctx.restore();
+      }
+    },
+
+    putBinaryImageData: function CanvasGraphics_putBinaryImageData(ctx,
+                                                                   imgData) {
+      var w = imgData.width, h = imgData.height;
       var tmpImgData = 'createImageData' in ctx ? ctx.createImageData(w, h) :
         ctx.getImageData(0, 0, w, h);
 
       var tmpImgDataPixels = tmpImgData.data;
       var data = imgData.data;
       if ('set' in tmpImgDataPixels)
         tmpImgDataPixels.set(data);
       else {
@@ -14298,45 +14404,50 @@ var PartialEvaluator = (function Partial
           var decode = dict.get('Decode', 'D');
           var inverseDecode = !!decode && decode[0] > 0;
 
           fn = 'paintImageMaskXObject';
           args = [imgArray, inverseDecode, width, height];
           return;
         }
 
+        var softMask = dict.get('SMask', 'SM') || false;
+        var mask = dict.get('Mask') || false;
+
+        var SMALL_IMAGE_DIMENSIONS = 200;
+        // Inlining small images into the queue as RGB data
+        if (inline && !softMask && !mask &&
+            !(image instanceof JpegStream) &&
+            (w + h) < SMALL_IMAGE_DIMENSIONS) {
+          var imageObj = new PDFImage(xref, resources, image,
+                                      inline, null, null);
+          var imgData = imageObj.getImageData();
+          fn = 'paintInlineImageXObject';
+          args = [imgData];
+          return;
+        }
+
         // If there is no imageMask, create the PDFImage and a lot
         // of image processing can be done here.
         var objId = 'img_' + uniquePrefix + (++self.objIdCounter);
         insertDependency([objId]);
         args = [objId, w, h];
 
-        var softMask = dict.get('SMask', 'SM') || false;
-        var mask = dict.get('Mask') || false;
-
         if (!softMask && !mask && image instanceof JpegStream &&
             image.isNativelySupported(xref, resources)) {
           // These JPEGs don't need any more processing so we can just send it.
           fn = 'paintJpegXObject';
           handler.send('obj', [objId, pageIndex, 'JpegStream', image.getIR()]);
           return;
         }
 
         fn = 'paintImageXObject';
 
         PDFImage.buildImage(function(imageObj) {
-            var drawWidth = imageObj.drawWidth;
-            var drawHeight = imageObj.drawHeight;
-            var imgData = {
-              width: drawWidth,
-              height: drawHeight,
-              data: new Uint8Array(drawWidth * drawHeight * 4)
-            };
-            var pixels = imgData.data;
-            imageObj.fillRgbaBuffer(pixels, drawWidth, drawHeight);
+            var imgData = imageObj.getImageData();
             handler.send('obj', [objId, pageIndex, 'Image', imgData]);
           }, handler, xref, resources, image, inline);
       }
 
       if (!queue)
         queue = {};
 
       if (!queue.argsArray) {
@@ -14550,16 +14661,132 @@ var PartialEvaluator = (function Partial
           assertWellFormed(args.length <= 33, 'Too many arguments');
           args.push(obj instanceof Dict ? obj.getAll() : obj);
         }
       }
 
       return queue;
     },
 
+    optimizeQueue: function PartialEvaluator_optimizeQueue(queue) {
+      var fnArray = queue.fnArray, argsArray = queue.argsArray;
+      // grouping paintInlineImageXObject's into paintInlineImageXObjectGroup
+      // searching for (save, transform, paintInlineImageXObject, restore)+
+      var MIN_IMAGES_IN_INLINE_IMAGES_BLOCK = 10;
+      var MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200;
+      var MAX_WIDTH = 1000;
+      var IMAGE_PADDING = 1;
+      for (var i = 0, ii = fnArray.length; i < ii; i++) {
+        if (fnArray[i] === 'paintInlineImageXObject' &&
+            fnArray[i - 2] === 'save' && fnArray[i - 1] === 'transform' &&
+            fnArray[i + 1] === 'restore') {
+          var j = i - 2;
+          for (i += 2; i < ii && fnArray[i - 4] === fnArray[i]; i++) {
+          }
+          var count = Math.min((i - j) >> 2,
+                               MAX_IMAGES_IN_INLINE_IMAGES_BLOCK);
+          if (count < MIN_IMAGES_IN_INLINE_IMAGES_BLOCK) {
+            continue;
+          }
+          // assuming that heights of those image is too small (~1 pixel)
+          // packing as much as possible by lines
+          var maxX = 0;
+          var map = [], maxLineHeight = 0;
+          var currentX = IMAGE_PADDING, currentY = IMAGE_PADDING;
+          for (var q = 0; q < count; q++) {
+            var transform = argsArray[j + (q << 2) + 1];
+            var img = argsArray[j + (q << 2) + 2][0];
+            if (currentX + img.width > MAX_WIDTH) {
+              // starting new line
+              maxX = Math.max(maxX, currentX);
+              currentY += maxLineHeight + 2 * IMAGE_PADDING;
+              currentX = 0;
+              maxLineHeight = 0;
+            }
+            map.push({
+              transform: transform,
+              x: currentX, y: currentY,
+              w: img.width, h: img.height
+            });
+            currentX += img.width + 2 * IMAGE_PADDING;
+            maxLineHeight = Math.max(maxLineHeight, img.height);
+          }
+          var imgWidth = Math.max(maxX, currentX) + IMAGE_PADDING;
+          var imgHeight = currentY + maxLineHeight + IMAGE_PADDING;
+          var imgData = new Uint8Array(imgWidth * imgHeight * 4);
+          var imgRowSize = imgWidth << 2;
+          for (var q = 0; q < count; q++) {
+            var data = argsArray[j + (q << 2) + 2][0].data;
+            // copy image by lines and extends pixels into padding
+            var rowSize = map[q].w << 2;
+            var dataOffset = 0;
+            var offset = (map[q].x + map[q].y * imgWidth) << 2;
+            imgData.set(
+              data.subarray(0, rowSize), offset - imgRowSize);
+            for (var k = 0, kk = map[q].h; k < kk; k++) {
+              imgData.set(
+                data.subarray(dataOffset, dataOffset + rowSize), offset);
+              dataOffset += rowSize;
+              offset += imgRowSize;
+            }
+            imgData.set(
+              data.subarray(dataOffset - rowSize, dataOffset), offset);
+            while (offset >= 0) {
+              data[offset - 4] = data[offset];
+              data[offset - 3] = data[offset + 1];
+              data[offset - 2] = data[offset + 2];
+              data[offset - 1] = data[offset + 3];
+              data[offset + rowSize] = data[offset + rowSize - 4];
+              data[offset + rowSize + 1] = data[offset + rowSize - 3];
+              data[offset + rowSize + 2] = data[offset + rowSize - 2];
+              data[offset + rowSize + 3] = data[offset + rowSize - 1];
+              offset -= imgRowSize;
+            }
+          }
+          // replacing queue items
+          fnArray.splice(j, count * 4, ['paintInlineImageXObjectGroup']);
+          argsArray.splice(j, count * 4,
+            [{width: imgWidth, height: imgHeight, data: imgData}, map]);
+          i = j;
+          ii = fnArray.length;
+        }
+      }
+      // grouping paintImageMaskXObject's into paintImageMaskXObjectGroup
+      // searching for (save, transform, paintImageMaskXObject, restore)+
+      var MIN_IMAGES_IN_MASKS_BLOCK = 10;
+      var MAX_IMAGES_IN_MASKS_BLOCK = 100;
+      for (var i = 0, ii = fnArray.length; i < ii; i++) {
+        if (fnArray[i] === 'paintImageMaskXObject' &&
+            fnArray[i - 2] === 'save' && fnArray[i - 1] === 'transform' &&
+            fnArray[i + 1] === 'restore') {
+          var j = i - 2;
+          for (i += 2; i < ii && fnArray[i - 4] === fnArray[i]; i++) {
+          }
+          var count = Math.min((i - j) >> 2,
+                               MAX_IMAGES_IN_MASKS_BLOCK);
+          if (count < MIN_IMAGES_IN_MASKS_BLOCK) {
+            continue;
+          }
+          var images = [];
+          for (var q = 0; q < count; q++) {
+            var transform = argsArray[j + (q << 2) + 1];
+            var maskParams = argsArray[j + (q << 2) + 2];
+            images.push({data: maskParams[0], width: maskParams[2],
+              height: maskParams[3], transform: transform,
+              inverseDecode: maskParams[1]});
+          }
+          // replacing queue items
+          fnArray.splice(j, count * 4, ['paintImageMaskXObjectGroup']);
+          argsArray.splice(j, count * 4, [images]);
+          i = j;
+          ii = fnArray.length;
+        }
+      }
+    },
+
     getTextContent: function PartialEvaluator_getTextContent(
                                                     stream, resources, state) {
       var bidiTexts;
       var SPACE_FACTOR = 0.35;
       var MULTI_SPACE_FACTOR = 1.5;
 
       if (!state) {
         bidiTexts = [];
@@ -17239,16 +17466,21 @@ var Font = (function FontClosure() {
     }
 
     // Some fonts might use wrong font types for Type1C or CIDFontType0C
     var subtype = properties.subtype;
     if (subtype == 'Type1C' && (type != 'Type1' && type != 'MMType1'))
       type = 'Type1';
     if (subtype == 'CIDFontType0C' && type != 'CIDFontType0')
       type = 'CIDFontType0';
+    // XXX: Temporarily change the type for open type so we trigger a warning.
+    // This should be removed when we add support for open type.
+    if (subtype === 'OpenType') {
+      type = 'OpenType';
+    }
 
     var data;
     switch (type) {
       case 'Type1':
       case 'CIDFontType0':
         this.mimetype = 'font/opentype';
 
         var cff = (subtype == 'Type1C' || subtype == 'CIDFontType0C') ?
@@ -17263,17 +17495,17 @@ var Font = (function FontClosure() {
         this.mimetype = 'font/opentype';
 
         // Repair the TrueType file. It is can be damaged in the point of
         // view of the sanitizer
         data = this.checkAndRepair(name, file, properties);
         break;
 
       default:
-        warn('Font ' + properties.type + ' is not supported');
+        warn('Font ' + type + ' is not supported');
         break;
     }
 
     this.data = data;
     this.fontMatrix = properties.fontMatrix;
     this.widthMultiplier = !properties.fontMatrix ? 1.0 :
       1.0 / properties.fontMatrix[0];
     this.encoding = properties.baseEncoding;
@@ -19438,21 +19670,31 @@ var Type1Parser = function type1Parser()
         index--;
       } else {
         warn('Malformed charsting stack: found bad token ' + token + '.');
       }
     }
     return args;
   }
 
+  // Remove the same number of args from the stack that are in the args
+  // parameter. Args should be built from breakUpArgs().
+  function popArgs(stack, args) {
+    for (var i = 0, ii = args.length; i < ii; i++) {
+      for (var j = 0, jj = args[i].arg.length; j < jj; j++) {
+        stack.pop();
+      }
+    }
+  }
+
   function decodeCharString(array) {
     var charstring = [];
     var lsb = 0;
     var width = 0;
-    var flexState = 0;
+    var flexing = false;
 
     var value = '';
     var count = array.length;
     for (var i = 0; i < count; i++) {
       value = array[i];
 
       if (value < 32) {
         var command = null;
@@ -19526,37 +19768,70 @@ var Type1Parser = function type1Parser()
             // To convert to type2 we have to move the width value to the first
             // part of the charstring and then use hmoveto with lsb.
             charstring = arg1.arg;
             charstring = charstring.concat(arg0.arg);
             charstring.push('hmoveto');
             continue;
           } else if (value == 10) { // callsubr
             if (charstring[charstring.length - 1] < 3) { // subr #0..2
+              // XXX: According to the spec if flex or hinting is not used then
+              // subroutines 0-3 can actually be anything defined by the font,
+              // so we really shouldn't be doing flex here but when
+              // callothersubr 0-2 is used. There hasn't been a real world
+              // example of this yet so we'll keep doing it here.
               var subrNumber = charstring.pop();
               switch (subrNumber) {
                 case 1:
-                  flexState = 1; // prepare for flex coordinates
-                  break;
-                case 2:
-                  flexState = 2; // flex in progress
+                  flexing = true; // prepare for flex coordinates
                   break;
                 case 0:
-                  // type2 flex command does not need final coords
-                  charstring.push('exch', 'drop', 'exch', 'drop');
-                  charstring.push('flex');
-                  flexState = 0;
+                  var flexArgs = breakUpArgs(charstring, 17);
+                  popArgs(charstring, flexArgs);
+
+                  charstring.push(
+                    flexArgs[2].value + flexArgs[0].value, // bcp1x + rpx
+                    flexArgs[3].value + flexArgs[1].value, // bcp1y + rpy
+                    flexArgs[4].value, // bcp2x
+                    flexArgs[5].value, // bcp2y
+                    flexArgs[6].value, // p2x
+                    flexArgs[7].value, // p2y
+                    flexArgs[8].value, // bcp3x
+                    flexArgs[9].value, // bcp3y
+                    flexArgs[10].value, // bcp4x
+                    flexArgs[11].value, // bcp4y
+                    flexArgs[12].value, // p3x
+                    flexArgs[13].value, // p3y
+                    flexArgs[14].value, // flexDepth
+                    // 15 = finalx unused by flex
+                    // 16 = finaly unused by flex
+                    'flex'
+                  );
+
+                  flexing = false;
                   break;
               }
               continue;
             }
-          } else if (value == 21 && flexState > 0) {
-            if (flexState > 1)
-              continue; // ignoring rmoveto
-            value = 5; // first segment replacing with rlineto
+          } else if (value == 21 && flexing) { // rmoveto
+            continue; // ignoring rmoveto
+          } else if (value == 22 && flexing) { // hmoveto
+            // Add the dy for flex.
+            charstring.push(0);
+            continue; // ignoring hmoveto
+          } else if (value == 4 && flexing) { // vmoveto
+            // Add the dx for flex and but also swap the values so they are the
+            // right order.
+            var vArgs = breakUpArgs(charstring, 1);
+            popArgs(charstring, vArgs);
+            charstring.push(0);
+            for (var t = 0, tt = vArgs[0].arg.length; t < tt; t++) {
+              charstring.push(vArgs[0].arg[t]);
+            }
+            continue; // ignoring vmoveto
           } else if (!HINTING_ENABLED && (value == 1 || value == 3)) {
             charstring.push('drop', 'drop');
             continue;
           }
           command = charStringDictionary[value];
         }
 
         // Some charstring commands are meaningless in Type2 and will return
@@ -19938,71 +20213,16 @@ var Type1Font = function Type1Font(name,
   var subrs = this.getType2Subrs(data.subrs);
 
   this.charstrings = charstrings;
   this.data = this.wrap(name, type2Charstrings, this.charstrings,
                         subrs, properties);
 };
 
 Type1Font.prototype = {
-  createCFFIndexHeader: function Type1Font_createCFFIndexHeader(objects,
-                                                                isByte) {
-    // First 2 bytes contains the number of objects contained into this index
-    var count = objects.length;
-
-    // If there is no object, just create an array saying that with another
-    // offset byte.
-    if (count == 0)
-      return '\x00\x00\x00';
-
-    var data = String.fromCharCode((count >> 8) & 0xFF, count & 0xff);
-
-    // Next byte contains the offset size use to reference object in the file
-    // Actually we're using 0x04 to be sure to be able to store everything
-    // without thinking of it while coding.
-    data += '\x04';
-
-    // Add another offset after this one because we need a new offset
-    var relativeOffset = 1;
-    for (var i = 0; i < count + 1; i++) {
-      data += String.fromCharCode((relativeOffset >>> 24) & 0xFF,
-                                  (relativeOffset >> 16) & 0xFF,
-                                  (relativeOffset >> 8) & 0xFF,
-                                  relativeOffset & 0xFF);
-
-      if (objects[i])
-        relativeOffset += objects[i].length;
-    }
-
-    for (var i = 0; i < count; i++) {
-      for (var j = 0, jj = objects[i].length; j < jj; j++)
-        data += isByte ? String.fromCharCode(objects[i][j] & 0xFF) :
-                objects[i][j];
-    }
-    return data;
-  },
-
-  encodeNumber: function Type1Font_encodeNumber(value) {
-    // some of the fonts has ouf-of-range values
-    // they are just arithmetic overflows
-    // make sanitizer happy
-    value |= 0;
-    if (value >= -32768 && value <= 32767) {
-      return '\x1c' +
-             String.fromCharCode((value >> 8) & 0xFF) +
-             String.fromCharCode(value & 0xFF);
-    } else {
-      return '\x1d' +
-             String.fromCharCode((value >> 24) & 0xFF) +
-             String.fromCharCode((value >> 16) & 0xFF) +
-             String.fromCharCode((value >> 8) & 0xFF) +
-             String.fromCharCode(value & 0xFF);
-    }
-  },
-
   getOrderedCharStrings: function Type1Font_getOrderedCharStrings(glyphs,
                                                             properties) {
     var charstrings = [];
     var i, length, glyphName;
     var unusedUnicode = CMAP_GLYPH_OFFSET;
     for (i = 0, length = glyphs.length; i < length; i++) {
       var item = glyphs[i];
       var glyphName = item.glyph;
@@ -20111,161 +20331,109 @@ Type1Font.prototype = {
         } else
           charstring.splice(i, 1); // ignoring empty subr call
         i--;
       } else {
         // Type1 charstring use a division for number above 32000
         if (command > 32000) {
           var divisor = charstring[i + 1];
           command /= divisor;
-          charstring.splice(i, 3, 28, command >> 8, command & 0xff);
-        } else {
-          charstring.splice(i, 1, 28, command >> 8, command & 0xff);
+          charstring.splice(i, 3, 28, (command >> 8) & 0xff, command & 0xff);
+        } else {
+          charstring.splice(i, 1, 28, (command >> 8) & 0xff, command & 0xff);
         }
         i += 2;
       }
     }
     return charstring;
   },
 
   wrap: function Type1Font_wrap(name, glyphs, charstrings, subrs, properties) {
-    var fields = {
-      // major version, minor version, header size, offset size
-      'header': '\x01\x00\x04\x04',
-
-      'names': this.createCFFIndexHeader([name]),
-
-      'topDict': (function topDict(self) {
-        return function cffWrapTopDict() {
-          var header = '\x00\x01\x01\x01';
-          var dict =
-              '\xf8\x1b\x00' + // version
-              '\xf8\x1c\x01' + // Notice
-              '\xf8\x1d\x02' + // FullName
-              '\xf8\x1e\x03' + // FamilyName
-              '\xf8\x1f\x04' +  // Weight
-              '\x1c\x00\x00\x10'; // Encoding
-
-          var boundingBox = properties.bbox;
-          for (var i = 0, ii = boundingBox.length; i < ii; i++)
-            dict += self.encodeNumber(boundingBox[i]);
-          dict += '\x05'; // FontBBox;
-
-          var offset = fields.header.length +
-                       fields.names.length +
-                       (header.length + 1) +
-                       (dict.length + (4 + 4)) +
-                       fields.strings.length +
-                       fields.globalSubrs.length;
-
-          // If the offset if over 32767, encodeNumber is going to return
-          // 5 bytes to encode the position instead of 3.
-          if ((offset + fields.charstrings.length) > 32767) {
-            offset += 9;
-          } else {
-            offset += 7;
-          }
-
-          dict += self.encodeNumber(offset) + '\x0f'; // Charset
-
-          offset = offset + (glyphs.length * 2) + 1;
-          dict += self.encodeNumber(offset) + '\x11'; // Charstrings
-
-          offset = offset + fields.charstrings.length;
-          dict += self.encodeNumber(fields.privateData.length);
-          dict += self.encodeNumber(offset) + '\x12'; // Private
-
-          return header + String.fromCharCode(dict.length + 1) + dict;
-        };
-      })(this),
-
-      'strings': (function strings(self) {
-        var strings = [
-          'Version 0.11',         // Version
-          'See original notice',  // Notice
-          name,                   // FullName
-          name,                   // FamilyName
-          'Medium'                // Weight
-        ];
-        return self.createCFFIndexHeader(strings);
-      })(this),
-
-      'globalSubrs': this.createCFFIndexHeader([]),
-
-      'charset': (function charset(self) {
-        var charsetString = '\x00'; // Encoding
-
-        var count = glyphs.length;
-        for (var i = 0; i < count; i++) {
-          var index = CFFStandardStrings.indexOf(charstrings[i].glyph);
-          // Some characters like asterikmath && circlecopyrt are
-          // missing from the original strings, for the moment let's
-          // map them to .notdef and see later if it cause any
-          // problems
-          if (index == -1)
-            index = 0;
-
-          charsetString += String.fromCharCode(index >> 8, index & 0xff);
-        }
-        return charsetString;
-      })(this),
-
-      'charstrings': this.createCFFIndexHeader([[0x8B, 0x0E]].concat(glyphs),
-                                               true),
-
-      'privateData': (function cffWrapPrivate(self) {
-        var data =
-            '\x8b\x14' + // defaultWidth
-            '\x8b\x15';  // nominalWidth
-        var fieldMap = {
-          BlueValues: '\x06',
-          OtherBlues: '\x07',
-          FamilyBlues: '\x08',
-          FamilyOtherBlues: '\x09',
-          StemSnapH: '\x0c\x0c',
-          StemSnapV: '\x0c\x0d',
-          BlueShift: '\x0c\x0a',
-          BlueFuzz: '\x0c\x0b',
-          BlueScale: '\x0c\x09',
-          LanguageGroup: '\x0c\x11',
-          ExpansionFactor: '\x0c\x18'
-        };
-        for (var field in fieldMap) {
-          if (!properties.privateData.hasOwnProperty(field))
-            continue;
-          var value = properties.privateData[field];
-
-          if (isArray(value)) {
-            data += self.encodeNumber(value[0]);
-            for (var i = 1, ii = value.length; i < ii; i++)
-              data += self.encodeNumber(value[i] - value[i - 1]);
-          } else {
-            data += self.encodeNumber(value);
-          }
-          data += fieldMap[field];
-        }
-
-        data += self.encodeNumber(data.length + 4) + '\x13'; // Subrs offset
-
-        return data;
-      })(this),
-
-      'localSubrs': this.createCFFIndexHeader(subrs, true)
-    };
-    fields.topDict = fields.topDict();
-
-
-    var cff = [];
-    for (var index in fields) {
-      var field = fields[index];
-      for (var i = 0, ii = field.length; i < ii; i++)
-        cff.push(field.charCodeAt(i));
-    }
-
-    return cff;
+    var cff = new CFF();
+    cff.header = new CFFHeader(1, 0, 4, 4);
+
+    cff.names = [name];
+
+    var topDict = new CFFTopDict();
+    topDict.setByName('version', 0);
+    topDict.setByName('Notice', 1);
+    topDict.setByName('FullName', 2);
+    topDict.setByName('FamilyName', 3);
+    topDict.setByName('Weight', 4);
+    topDict.setByName('Encoding', null); // placeholder
+    topDict.setByName('FontBBox', properties.bbox);
+    topDict.setByName('charset', null); // placeholder
+    topDict.setByName('CharStrings', null); // placeholder
+    topDict.setByName('Private', null); // placeholder
+    cff.topDict = topDict;
+
+    var strings = new CFFStrings();
+    strings.add('Version 0.11'); // Version
+    strings.add('See original notice'); // Notice
+    strings.add(name); // FullName
+    strings.add(name); // FamilyName
+    strings.add('Medium'); // Weight
+    cff.strings = strings;
+
+    cff.globalSubrIndex = new CFFIndex();
+
+    var count = glyphs.length;
+    var charsetArray = [0];
+    for (var i = 0; i < count; i++) {
+      var index = CFFStandardStrings.indexOf(charstrings[i].glyph);
+      // Some characters like asterikmath && circlecopyrt are
+      // missing from the original strings, for the moment let's
+      // map them to .notdef and see later if it cause any
+      // problems
+      if (index == -1)
+        index = 0;
+
+      charsetArray.push((index >> 8) & 0xff, index & 0xff);
+    }
+    cff.charset = new CFFCharset(false, 0, [], charsetArray);
+
+    var charStringsIndex = new CFFIndex();
+    charStringsIndex.add([0x8B, 0x0E]); // .notdef
+    for (var i = 0; i < count; i++) {
+      charStringsIndex.add(glyphs[i]);
+    }
+    cff.charStrings = charStringsIndex;
+
+    var privateDict = new CFFPrivateDict();
+    privateDict.setByName('Subrs', null); // placeholder
+    var fields = [
+      // TODO: missing StdHW, StdVW, ForceBold
+      'BlueValues',
+      'OtherBlues',
+      'FamilyBlues',
+      'FamilyOtherBlues',
+      'StemSnapH',
+      'StemSnapV',
+      'BlueShift',
+      'BlueFuzz',
+      'BlueScale',
+      'LanguageGroup',
+      'ExpansionFactor'
+    ];
+    for (var i = 0, ii = fields.length; i < ii; i++) {
+      var field = fields[i];
+      if (!properties.privateData.hasOwnProperty(field))
+        continue;
+      privateDict.setByName(field, properties.privateData[field]);
+    }
+    cff.topDict.privateDict = privateDict;
+
+    var subrIndex = new CFFIndex();
+    for (var i = 0, ii = subrs.length; i < ii; i++) {
+      subrIndex.add(subrs[i]);
+    }
+    privateDict.subrsIndex = subrIndex;
+
+    var compiler = new CFFCompiler(cff);
+    return compiler.compile();
   }
 };
 
 var CFFFont = (function CFFFontClosure() {
   function CFFFont(file, properties) {
     this.properties = properties;
 
     var parser = new CFFParser(file, properties);
@@ -20716,17 +20884,16 @@ var CFFParser = (function CFFParserClosu
             } else {
               validationCommand = CharstringValidationData12[q];
             }
           } else if (value === 28) { // number (16 bit)
             j += 2;
             stackSize++;
           } else if (value == 14) {
             if (stackSize >= 4) {
-              // TODO fix deprecated endchar construct for Windows
               stackSize -= 4;
             }
           } else if (value >= 32 && value <= 246) {  // number
             stackSize++;
           } else if (value >= 247 && value <= 254) {  // number (+1 bytes)
             j++;
             stackSize++;
           } else if (value == 255) {  // number (32 bit)
@@ -20878,23 +21045,23 @@ var CFFParser = (function CFFParserClosu
           var sid = (bytes[pos++] << 8) + (bytes[pos++] & 0xff);
           encoding[code] = properties.differences.indexOf(strings.get(sid));
         }
       }
 
       if (pos == 0 || pos == 1) {
         predefined = true;
         format = pos;
-        var gid = 1;
         var baseEncoding = pos ? Encodings.ExpertEncoding :
                                  Encodings.StandardEncoding;
         for (var i = 0, ii = charset.length; i < ii; i++) {
           var index = baseEncoding.indexOf(charset[i]);
-          if (index != -1)
-            encoding[index] = gid++;
+          if (index != -1) {
+            encoding[index] = i;
+          }
         }
       } else {
         var dataStart = pos;
         var format = bytes[pos++];
         switch (format & 0x7f) {
           case 0:
             var glyphsCount = bytes[pos++];
             for (var i = 1; i <= glyphsCount; i++)
@@ -21066,16 +21233,22 @@ var CFFDict = (function CFFDictClosure()
         return true;
       var type = this.types[key];
       // remove the array wrapping these types of values
       if (type === 'num' || type === 'sid' || type === 'offset')
         value = value[0];
       this.values[key] = value;
       return true;
     },
+    setByName: function CFFDict_setByName(name, value) {
+      if (!(name in this.nameToKeyMap)) {
+        error('Invalid dictionary name "' + name + '"');
+      }
+      this.values[this.nameToKeyMap[name]] = value;
+    },
     hasName: function CFFDict_hasName(name) {
       return this.nameToKeyMap[name] in this.values;
     },
     getByName: function CFFDict_getByName(name) {
       if (!(name in this.nameToKeyMap))
         error('Invalid dictionary name "' + name + '"');
       var key = this.nameToKeyMap[name];
       if (!(key in this.values))
@@ -26265,16 +26438,28 @@ var PDFImage = (function PDFImageClosure
 
       var comps = this.getComponents(imgArray);
       var length = width * height;
       // we aren't using a colorspace so we need to scale the value
       var scale = 255 / ((1 << bpc) - 1);
       for (var i = 0; i < length; ++i)
         buffer[i] = (scale * comps[i]) | 0;
     },
+    getImageData: function PDFImage_getImageData() {
+      var drawWidth = this.drawWidth;
+      var drawHeight = this.drawHeight;
+      var imgData = {
+        width: drawWidth,
+        height: drawHeight,
+        data: new Uint8Array(drawWidth * drawHeight * 4)
+      };
+      var pixels = imgData.data;
+      this.fillRgbaBuffer(pixels, drawWidth, drawHeight);
+      return imgData;
+    },
     getImageBytes: function PDFImage_getImageBytes(length) {
       this.image.reset();
       return this.image.getBytes(length);
     }
   };
   return PDFImage;
 })();
 
@@ -29238,17 +29423,16 @@ function isEOF(v) {
   return v == EOF;
 }
 
 var Parser = (function ParserClosure() {
   function Parser(lexer, allowStreams, xref) {
     this.lexer = lexer;
     this.allowStreams = allowStreams;
     this.xref = xref;
-    this.inlineImg = 0;
     this.refill();
   }
 
   Parser.prototype = {
     refill: function Parser_refill() {
       this.buf1 = this.lexer.getObj();
       this.buf2 = this.lexer.getObj();
     },
@@ -29363,25 +29547,16 @@ var Parser = (function ParserClosure() {
             state = state === 2 ? 3 : 0;
             break;
           default:
             state = 0;
             break;
         }
       }
 
-      // TODO improve the small images performance to remove the limit
-      var inlineImgLimit = 500;
-      if (++this.inlineImg >= inlineImgLimit) {
-        if (this.inlineImg === inlineImgLimit)
-          warn('Too many inline images');
-        this.shift();
-        return null;
-      }
-
       var length = (stream.pos - 4) - startPos;
       var imageStream = stream.makeSubStream(startPos, length, dict);
       if (cipherTransform)
         imageStream = cipherTransform.createStream(imageStream);
       imageStream = this.filter(imageStream, dict, length);
       imageStream.parameters = dict;
 
       this.buf2 = Cmd.get('EI');
@@ -30249,17 +30424,18 @@ var TilingPattern = (function TilingPatt
 
   return TilingPattern;
 })();
 
 
 
 var Stream = (function StreamClosure() {
   function Stream(arrayBuffer, start, length, dict) {
-    this.bytes = new Uint8Array(arrayBuffer);
+    this.bytes = arrayBuffer instanceof Uint8Array ? arrayBuffer :
+      new Uint8Array(arrayBuffer);
     this.start = start || 0;
     this.pos = this.start;
     this.end = (start + length) || this.bytes.length;
     this.dict = dict;
   }
 
   // required methods for a stream. if a particular stream does not
   // implement these, an error should be thrown
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -516,17 +516,17 @@ html[dir='ltr'] .splitToolbarButtonSepar
   float:left;
 }
 html[dir='rtl'] .splitToolbarButtonSeparator {
   float:right;
 }
 .splitToolbarButton:hover > .splitToolbarButtonSeparator,
 .splitToolbarButton.toggled > .splitToolbarButtonSeparator {
   padding: 12px 0;
-  margin: 0;
+  margin: 1px 0;
   box-shadow: 0 0 0 1px hsla(0,0%,100%,.03);
   -webkit-transition-property: padding;
   -webkit-transition-duration: 10ms;
   -webkit-transition-timing-function: ease;
   -moz-transition-property: padding;
   -moz-transition-duration: 10ms;
   -moz-transition-timing-function: ease;
   -ms-transition-property: padding;
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -931,16 +931,31 @@ var PDFView = {
     support = FirefoxCom.requestSync('supportsIntegratedFind');
     Object.defineProperty(this, 'supportsIntegratedFind', { value: support,
                                                             enumerable: true,
                                                             configurable: true,
                                                             writable: false });
     return support;
   },
 
+  get supportsDocumentFonts() {
+    var support = true;
+    support = FirefoxCom.requestSync('supportsDocumentFonts');
+    Object.defineProperty(this, 'supportsDocumentFonts', { value: support,
+                                                           enumerable: true,
+                                                           configurable: true,
+                                                           writable: false });
+    return support;
+  },
+
+  get isHorizontalScrollbarEnabled() {
+    var div = document.getElementById('viewerContainer');
+    return div.scrollWidth > div.clientWidth;
+  },
+
   initPassiveLoading: function pdfViewInitPassiveLoading() {
     if (!PDFView.loadingBar) {
       PDFView.loadingBar = new ProgressBar('#loadingBar', {});
     }
 
     window.addEventListener('message', function window_message(e) {
       var args = e.data;
 
@@ -2032,28 +2047,36 @@ var PageView = function pageView(contain
     var ctx = canvas.getContext('2d');
     ctx.save();
     ctx.fillStyle = 'rgb(255, 255, 255)';
     ctx.fillRect(0, 0, canvas.width, canvas.height);
     ctx.restore();
     if (outputScale.scaled) {
       ctx.scale(outputScale.sx, outputScale.sy);
     }
+    // Checking if document fonts are used only once
+    var checkIfDocumentFontsUsed = !PDFView.pdfDocument.embeddedFontsUsed;
 
     // Rendering area
 
     var self = this;
     function pageViewDrawCallback(error) {
       self.renderingState = RenderingStates.FINISHED;
 
       if (self.loadingIconDiv) {
         div.removeChild(self.loadingIconDiv);
         delete self.loadingIconDiv;
       }
 
+      if (checkIfDocumentFontsUsed && PDFView.pdfDocument.embeddedFontsUsed &&
+          !PDFView.supportsDocumentFonts) {
+        console.error(mozL10n.get('web_fonts_disabled', null,
+          'Web fonts are disabled: unable to use embedded PDF fonts.'));
+        PDFView.fallback();
+      }
       if (error) {
         PDFView.error(mozL10n.get('rendering_error', null,
           'An error occurred while rendering the page.'), error);
       }
 
       self.stats = pdfPage.stats;
       self.updateStats();
       if (self.onAfterDraw)
@@ -3090,26 +3113,28 @@ window.addEventListener('keydown', funct
         if (!PDFView.supportsIntegratedFind) {
           PDFFindBar.toggle();
           handled = true;
         }
         break;
       case 61: // FF/Mac '='
       case 107: // FF '+' and '='
       case 187: // Chrome '+'
+      case 171: // FF with German keyboard
         PDFView.zoomIn();
         handled = true;
         break;
       case 173: // FF/Mac '-'
       case 109: // FF '-'
       case 189: // Chrome '-'
         PDFView.zoomOut();
         handled = true;
         break;
       case 48: // '0'
+      case 96: // '0' on Numpad of Swedish keyboard
         PDFView.parseScale(DEFAULT_SCALE, true);
         handled = true;
         break;
     }
   }
 
   // CTRL or META with or without SHIFT.
   if (cmd == 1 || cmd == 8 || cmd == 5 || cmd == 12) {
@@ -3147,29 +3172,37 @@ window.addEventListener('keydown', funct
       case 38: // up arrow
       case 33: // pg up
       case 8: // backspace
         if (!PDFView.isFullscreen) {
           break;
         }
         //  in fullscreen mode falls throw here
       case 37: // left arrow
+        // horizontal scrolling using arrow keys
+        if (PDFView.isHorizontalScrollbarEnabled) {
+          break;
+        }
       case 75: // 'k'
       case 80: // 'p'
         PDFView.page--;
         handled = true;
         break;
       case 40: // down arrow
       case 34: // pg down
       case 32: // spacebar
         if (!PDFView.isFullscreen) {
           break;
         }
         //  in fullscreen mode falls throw here
       case 39: // right arrow
+        // horizontal scrolling using arrow keys
+        if (PDFView.isHorizontalScrollbarEnabled) {
+          break;
+        }
       case 74: // 'j'
       case 78: // 'n'
         PDFView.page++;
         handled = true;
         break;
 
       case 36: // home
         if (PDFView.isFullscreen) {
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -337,18 +337,20 @@
 @BINPATH@/components/nsBrowserGlue.js
 @BINPATH@/components/nsSetDefaultBrowser.manifest
 @BINPATH@/components/nsSetDefaultBrowser.js
 @BINPATH@/components/BrowserDownloads.manifest
 @BINPATH@/components/DownloadsStartup.js
 @BINPATH@/components/DownloadsUI.js
 @BINPATH@/components/BrowserPlaces.manifest
 @BINPATH@/components/BrowserPageThumbs.manifest
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
 @BINPATH@/components/nsPrivateBrowsingService.manifest
 @BINPATH@/components/nsPrivateBrowsingService.js
+#endif
 @BINPATH@/components/SiteSpecificUserAgent.js
 @BINPATH@/components/SiteSpecificUserAgent.manifest
 @BINPATH@/components/toolkitsearch.manifest
 @BINPATH@/components/nsSearchService.js
 @BINPATH@/components/nsSearchSuggestions.js
 @BINPATH@/components/passwordmgr.manifest
 @BINPATH@/components/nsLoginInfo.js
 @BINPATH@/components/nsLoginManager.js
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -436,21 +436,23 @@ identity.newIdentity.description = Enter
 identity.next.label = Next
 identity.next.accessKey = n
 # LOCALIZATION NOTE: shown in the popup notification when a user successfully logs into a website
 # LOCALIZATION NOTE (identity.loggedIn.description): %S is the user's identity (e.g. user@example.com)
 identity.loggedIn.description = Signed in as: %S
 identity.loggedIn.signOut.label = Sign Out
 identity.loggedIn.signOut.accessKey = O
 
-# LOCALIZATION NOTE (getUserMedia.shareCamera.message, getUserMedia.shareMicrophone.message, getUserMedia.shareCameraAndMicrophone.message): %S is the website origin (e.g. www.mozilla.org)
-# LOCALIZATION NOTE (getUserMedia.shareMicrophone.message): %S is the website origin (e.g. www.mozilla.org)
+# LOCALIZATION NOTE (getUserMedia.shareCamera.message, getUserMedia.shareMicrophone.message, getUserMedia.shareCameraAndMicrophone.message, getUserMedia.sharingCamera.message, getUserMedia.sharingMicrophone.message, getUserMedia.sharingCameraAndMicrophone.message): %S is the website origin (e.g. www.mozilla.org)
 # LOCALIZATION NOTE (getUserMedia.shareSelectedDevices.label):
 # Semi-colon list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # The number of devices can be either one or two.
 getUserMedia.shareCamera.message = Would you like to share your camera with %S?
 getUserMedia.shareMicrophone.message = Would you like to share your microphone with %S?
 getUserMedia.shareCameraAndMicrophone.message = Would you like to share your camera and microphone with %S?
 getUserMedia.shareSelectedDevices.label = Share Selected Device;Share Selected Devices
 getUserMedia.shareSelectedDevices.accesskey = S
 getUserMedia.denyRequest.label = Don't Share
 getUserMedia.denyRequest.accesskey = D
+getUserMedia.sharingCamera.message = You are currently sharing your camera with %S.
+getUserMedia.sharingMicrophone.message = You are currently sharing your microphone with %S.
+getUserMedia.sharingCameraAndMicrophone.message = You are currently sharing your camera and microphone with %S.
--- a/browser/locales/en-US/chrome/browser/devtools/connection-screen.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/connection-screen.dtd
@@ -8,15 +8,15 @@
   - -->
 
 <!ENTITY title      "Connect">
 <!ENTITY header     "Connect to remote device">
 <!ENTITY host       "Host:">
 <!ENTITY port       "Port:">
 <!ENTITY connect    "Connect">
 <!ENTITY connecting "Connecting…">
-<!ENTITY availability "Available remote objects:">
-<!ENTITY remoteProcess "remote process">
+<!ENTITY availableTabs "Available remote tabs:">
+<!ENTITY availableProcesses "Available remote processes:">
 <!ENTITY connectionError "Error:">
 <!ENTITY errorTimeout "Error: connection timeout.">
 <!ENTITY errorRefused "Error: connection refused.">
 <!ENTITY errorUnexpected "Unexpected error.">
-<!ENTITY help "Firefox Developer Tools can debug remote devices (Firefox for Android and Firefox OS for example). Make sure that you have turned on the 'Debugger Server' option on the remote device. See <a target='_' href='https://developer.mozilla.org/en-US/docs/Tools/Debugger#Remote_Debugging'>documentation</a>.">
+<!ENTITY help "Firefox Developer Tools can debug remote devices (Firefox for Android and Firefox OS, for example). Make sure that you have turned on the 'Remote debugging' option in the remote device. See the <a target='_' href='https://developer.mozilla.org/docs/Tools/Debugger#Remote_Debugging'>documentation</a> for more.">
--- a/browser/locales/en-US/chrome/browser/devtools/connection-screen.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/connection-screen.properties
@@ -1,9 +1,9 @@
 # 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/.
 
 # LOCALIZATION NOTE : FILE This file contains the Remote Connection strings.
 # The Remote Connection window can reached from the "connect…" menuitem
 # in the Web Developer menu.
 
-remoteProcess=Remote Process
+mainProcess=Main Process
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -121,8 +121,13 @@ actualDiskCacheSize=Your web content cac
 #   e.g., "Your application cache is currently using 200 MB"
 #   %1$S = size
 #   %2$S = unit (MB, KB, etc.)
 actualAppCacheSize=Your application cache is currently using %1$S %2$S of disk space
 
 syncUnlink.title=Do you want to unlink your device?
 syncUnlink.label=This device will no longer be associated with your Sync account. All of your personal data, both on this device and in your Sync account, will remain intact.
 syncUnlinkConfirm.label=Unlink
+
+# LOCALIZATION NOTE (featureEnableRequiresRestart, featureDisableRequiresRestart, restartTitle): %S = brandShortName
+featureEnableRequiresRestart=%S must restart to enable this feature.
+featureDisableRequiresRestart=%S must restart to disable this feature.
+shouldRestartTitle=Restart %S
\ No newline at end of file
--- a/browser/locales/en-US/chrome/browser/quitDialog.properties
+++ b/browser/locales/en-US/chrome/browser/quitDialog.properties
@@ -1,15 +1,12 @@
 # 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/.
 
 quitDialogTitle=Quit %S
-restartDialogTitle=Restart %S
 
 quitTitle=&Quit
-restartTitle=&Restart
 cancelTitle=&Cancel
 saveTitle=&Save and Quit
 neverAsk=Do not ask next time
 message=Do you want %S to save your tabs and windows for the next time it starts?
 messageNoWindows=Do you want %S to save your tabs for the next time it starts?
-messageRestart=%S will try to restore your tabs and windows when it restarts.
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -115,8 +115,9 @@ invalid_file_error=Invalid or corrupted 
 # LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip.
 # "{{type}}" will be replaced with an annotation type from a list defined in
 # the PDF spec (32000-1:2008 Table 169 – Annotation types).
 # Some common types are e.g.: "Check", "Text", "Comment", "Note"
 text_annotation_type=[{{type}} Annotation]
 request_password=PDF is protected by a password:
 
 printing_not_supported=Warning: Printing is not fully supported by this browser.
+web_fonts_disabled=Web fonts are disabled: unable to use embedded PDF fonts.
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -34,16 +34,17 @@
     locale/browser/devtools/styleinspector.dtd        (%chrome/browser/devtools/styleinspector.dtd)
     locale/browser/devtools/webConsole.dtd            (%chrome/browser/devtools/webConsole.dtd)
     locale/browser/devtools/sourceeditor.properties   (%chrome/browser/devtools/sourceeditor.properties)
     locale/browser/devtools/sourceeditor.dtd          (%chrome/browser/devtools/sourceeditor.dtd)
     locale/browser/devtools/profiler.properties       (%chrome/browser/devtools/profiler.properties)
     locale/browser/devtools/layoutview.dtd            (%chrome/browser/devtools/layoutview.dtd)
     locale/browser/devtools/responsiveUI.properties   (%chrome/browser/devtools/responsiveUI.properties)
     locale/browser/devtools/toolbox.dtd            (%chrome/browser/devtools/toolbox.dtd)
+    locale/browser/devtools/toolbox.properties     (%chrome/browser/devtools/toolbox.properties)
     locale/browser/devtools/inspector.dtd          (%chrome/browser/devtools/inspector.dtd)
     locale/browser/devtools/connection-screen.dtd  (%chrome/browser/devtools/connection-screen.dtd)
     locale/browser/devtools/connection-screen.properties (%chrome/browser/devtools/connection-screen.properties)
     locale/browser/newTab.dtd                      (%chrome/browser/newTab.dtd)
     locale/browser/newTab.properties               (%chrome/browser/newTab.properties)
     locale/browser/openLocation.dtd                (%chrome/browser/openLocation.dtd)
     locale/browser/openLocation.properties         (%chrome/browser/openLocation.properties)
     locale/browser/pageInfo.dtd                    (%chrome/browser/pageInfo.dtd)
--- a/browser/modules/test/browser_taskbar_preview.js
+++ b/browser/modules/test/browser_taskbar_preview.js
@@ -51,31 +51,31 @@ function test() {
   });
 
   let currentSelectedTab = gBrowser.selectedTab;
   for each (let idx in [1,2,3,4]) {
     let preview = getPreviewForTab(gBrowser.tabs[0]);
     let canvas = createThumbnailSurface(preview);
     let ctx = canvas.getContext("2d");
     preview.controller.drawThumbnail(ctx, canvas.width, canvas.height);
-    ok(currentSelectedTab == gBrowser.selectedTab, "Drawing