Merge m-c to oak
authorRobert Strong <robert.bugzilla@gmail.com>
Thu, 16 Jul 2015 15:23:48 -0700
changeset 491455 778ef4b5e5e2b827c472e18a4260b27150d377b6
parent 491454 2d12058dd5f321d415a25484002ad00adfe8920b (current diff)
parent 279085 8d262d1d0ae51740523db8089b4562e93e0c0e41 (diff)
child 491456 4e4607d4c8ca4c35bf3fc1c45f44c5c397357ef5
push id47343
push userbmo:dothayer@mozilla.com
push dateWed, 01 Mar 2017 22:58:58 +0000
milestone42.0a1
Merge m-c to oak
CLOBBER
browser/app/profile/firefox.js
browser/components/loop/content/shared/libs/sdk.js
browser/devtools/debugger/test/browser_dbg_hit-counts-01.js
browser/devtools/debugger/test/browser_dbg_hit-counts-02.js
browser/devtools/debugger/test/browser_dbg_tracing-01.js
browser/devtools/debugger/test/browser_dbg_tracing-02.js
browser/devtools/debugger/test/browser_dbg_tracing-03.js
browser/devtools/debugger/test/browser_dbg_tracing-04.js
browser/devtools/debugger/test/browser_dbg_tracing-05.js
browser/devtools/debugger/test/browser_dbg_tracing-06.js
browser/devtools/debugger/test/browser_dbg_tracing-07.js
browser/devtools/debugger/test/browser_dbg_tracing-08.js
browser/devtools/debugger/test/code_tracing-01.js
browser/devtools/debugger/test/doc_same-line-functions.html
browser/devtools/debugger/test/doc_tracing-01.html
browser/devtools/debugger/views/tracer-view.js
build/mobile/robocop/robotium-solo-4.3.1.jar
configure.in
dom/media/tests/mochitest/long.js
dom/media/tests/mochitest/steeplechase_long.ini
dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined_long.html
dom/media/tests/mochitest/test_peerConnection_basicAudio_long.html
dom/media/tests/mochitest/test_peerConnection_basicVideo_long.html
dom/media/webspeech/recognition/models/dict/cmu07a.dic
dom/media/webspeech/recognition/models/en-us-semi/README
dom/media/webspeech/recognition/models/en-us-semi/feat.params
dom/media/webspeech/recognition/models/en-us-semi/mdef
dom/media/webspeech/recognition/models/en-us-semi/means
dom/media/webspeech/recognition/models/en-us-semi/mixture_weights
dom/media/webspeech/recognition/models/en-us-semi/noisedict
dom/media/webspeech/recognition/models/en-us-semi/sendump
dom/media/webspeech/recognition/models/en-us-semi/transition_matrices
dom/media/webspeech/recognition/models/en-us-semi/variances
gfx/thebes/gfxWindowsPlatform.cpp
intl/unicharutil/tools/gentransliterate.pl
mobile/android/base/resources/drawable-hdpi/larry.png
mobile/android/base/resources/drawable-hdpi/shield_disabled_doorhanger.png
mobile/android/base/resources/drawable-hdpi/shield_enabled_doorhanger.png
mobile/android/base/resources/drawable-hdpi/tab_panel_tab_globe.png
mobile/android/base/resources/drawable-xhdpi/larry.png
mobile/android/base/resources/drawable-xhdpi/shield_disabled_doorhanger.png
mobile/android/base/resources/drawable-xhdpi/shield_enabled_doorhanger.png
mobile/android/base/resources/drawable-xhdpi/tab_panel_tab_globe.png
mobile/android/base/resources/drawable-xxhdpi/shield_disabled_doorhanger.png
mobile/android/base/resources/drawable-xxhdpi/shield_enabled_doorhanger.png
mobile/android/base/resources/drawable-xxhdpi/tab_panel_tab_globe.png
testing/mochitest/runtests.py
testing/web-platform/meta/FileAPI/progress.html.ini
testing/web-platform/meta/IndexedDB/idbcursor_advance_index9.htm.ini
testing/web-platform/meta/IndexedDB/idbcursor_advance_objectstore5.htm.ini
testing/web-platform/meta/IndexedDB/idbcursor_continue_index8.htm.ini
testing/web-platform/meta/IndexedDB/idbcursor_continue_objectstore6.htm.ini
testing/web-platform/meta/html/syntax/parsing/template/additions-to-foster-parenting/template-is-a-foster-parent-element.html.ini
testing/web-platform/meta/html/syntax/parsing/template/additions-to-foster-parenting/template-is-not-a-foster-parent-element.html.ini
testing/web-platform/meta/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/generating-of-implied-end-tags.html.ini
testing/web-platform/meta/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-body-token.html.ini
testing/web-platform/meta/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-frameset-token.html.ini
testing/web-platform/meta/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-head-token.html.ini
testing/web-platform/meta/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-html-token.html.ini
testing/web-platform/meta/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/start-tag-body.html.ini
testing/web-platform/meta/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/start-tag-html.html.ini
testing/web-platform/meta/html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/template-end-tag-without-start-one.html.ini
testing/web-platform/meta/html/syntax/parsing/template/additions-to-the-in-head-insertion-mode/generating-of-implied-end-tags.html.ini
testing/web-platform/meta/html/syntax/parsing/template/additions-to-the-in-head-insertion-mode/template-end-tag-without-start-one.html.ini
testing/web-platform/meta/html/syntax/parsing/template/additions-to-the-in-table-insertion-mode/end-tag-table.html.ini
testing/web-platform/meta/html/syntax/parsing/template/appending-to-a-template/template-child-nodes.html.ini
testing/web-platform/meta/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-body-context.html.ini
testing/web-platform/meta/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-context.html.ini
testing/web-platform/meta/html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-row-context.html.ini
testing/web-platform/meta/shadow-dom/shadow-trees/upper-boundary-encapsulation/dom-tree-accessors-001.html.ini
testing/web-platform/meta/webstorage/storage_local_window_open_second.html.ini
testing/web-platform/meta/webstorage/storage_session_window_open_second.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/claim-using-registration.https.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/postmessage-msgport-to-client.https.html.ini
testing/web-platform/tests/FileAPI/progress.html
testing/web-platform/tests/html/semantics/scripting-1/the-template-element/testcommon.js
testing/web-platform/tests/resources/apisample-error-worker.js
testing/web-platform/tests/resources/apisample-worker.js
testing/web-platform/tests/resources/apisample.htm
testing/web-platform/tests/resources/apisample10.html
testing/web-platform/tests/resources/apisample11.html
testing/web-platform/tests/resources/apisample12.html
testing/web-platform/tests/resources/apisample13.html
testing/web-platform/tests/resources/apisample14.html
testing/web-platform/tests/resources/apisample15.html
testing/web-platform/tests/resources/apisample16.html
testing/web-platform/tests/resources/apisample2.htm
testing/web-platform/tests/resources/apisample3.htm
testing/web-platform/tests/resources/apisample4.htm
testing/web-platform/tests/resources/apisample5.htm
testing/web-platform/tests/resources/apisample6.html
testing/web-platform/tests/resources/apisample7.html
testing/web-platform/tests/resources/apisample8.html
testing/web-platform/tests/resources/apisample9.html
testing/web-platform/tests/webmessaging/Channel_MessagePort_initial_disabled.htm
testing/web-platform/tests/webmessaging/Channel_MessagePort_onmessage_start.htm
testing/web-platform/tests/webstorage/iframe/event_body_handler.html
testing/web-platform/tests/webstorage/iframe/event_setattribute_handler.html
testing/web-platform/tests/webstorage/iframe/local_change_item_iframe.html
testing/web-platform/tests/webstorage/iframe/local_set_item_clear_iframe.html
testing/web-platform/tests/webstorage/iframe/local_set_item_iframe.html
testing/web-platform/tests/webstorage/iframe/local_set_item_remove_iframe.html
testing/web-platform/tests/webstorage/iframe/session_change_item_iframe.html
testing/web-platform/tests/webstorage/iframe/session_set_item_clear_iframe.html
testing/web-platform/tests/webstorage/iframe/session_set_item_iframe.html
testing/web-platform/tests/webstorage/iframe/session_set_item_remove_iframe.html
testing/web-platform/tests/webstorage/storage_local_window_open_second.html
testing/web-platform/tests/webstorage/storage_session_window_open_second.html
testing/web-platform/tests/workers/MessagePort_initial_disabled.htm
testing/web-platform/tests/workers/MessagePort_onmessage_start.htm
toolkit/components/telemetry/Histograms.json
toolkit/devtools/server/actors/tracer.js
toolkit/devtools/server/tests/unit/test_originAttributesToCookieJar.js
toolkit/devtools/server/tests/unit/test_profiler_events-02.js
toolkit/devtools/server/tests/unit/test_sourcemaps-14.js
toolkit/devtools/server/tests/unit/test_sourcemaps-15.js
toolkit/devtools/server/tests/unit/test_trace_actor-01.js
toolkit/devtools/server/tests/unit/test_trace_actor-02.js
toolkit/devtools/server/tests/unit/test_trace_actor-03.js
toolkit/devtools/server/tests/unit/test_trace_actor-04.js
toolkit/devtools/server/tests/unit/test_trace_actor-05.js
toolkit/devtools/server/tests/unit/test_trace_actor-06.js
toolkit/devtools/server/tests/unit/test_trace_actor-07.js
toolkit/devtools/server/tests/unit/test_trace_actor-08.js
toolkit/devtools/server/tests/unit/test_trace_actor-09.js
toolkit/devtools/server/tests/unit/test_trace_actor-10.js
toolkit/devtools/server/tests/unit/test_trace_actor-11.js
toolkit/devtools/server/tests/unit/test_trace_actor-12.js
toolkit/devtools/server/tests/unit/test_trace_actor-13.js
toolkit/mozapps/installer/upload-files.mk
toolkit/mozapps/update/common/updatehelper.cpp
toolkit/mozapps/update/common/updatehelper.h
toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
toolkit/mozapps/update/updater/updater.cpp
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsAppRunner.h
xpcom/idl-parser/Makefile.in
xpcom/idl-parser/header.py
xpcom/idl-parser/moz.build
xpcom/idl-parser/runtests.py
xpcom/idl-parser/typelib.py
xpcom/idl-parser/xpidl.py
--- a/accessible/atk/AccessibleWrap.cpp
+++ b/accessible/atk/AccessibleWrap.cpp
@@ -29,17 +29,17 @@
 #include "mozilla/ArrayUtils.h"
 #include "nsXPCOMStrings.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIPersistentProperties2.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
-AccessibleWrap::EAvailableAtkSignals AccessibleWrap::gAvailableAtkSignals =
+MaiAtkObject::EAvailableAtkSignals MaiAtkObject::gAvailableAtkSignals =
   eUnknown;
 
 //defined in ApplicationAccessibleWrap.cpp
 extern "C" GType g_atk_hyperlink_impl_type;
 
 /* MaiAtkObject */
 
 enum {
@@ -1164,17 +1164,28 @@ AccessibleWrap::HandleAccEvent(AccEvent*
         AccStateChangeEvent* event = downcast_accEvent(aEvent);
         MAI_ATK_OBJECT(atkObj)->FireStateChangeEvent(event->GetState(),
                                                      event->IsStateEnabled());
         break;
       }
 
     case nsIAccessibleEvent::EVENT_TEXT_REMOVED:
     case nsIAccessibleEvent::EVENT_TEXT_INSERTED:
-        return FireAtkTextChangedEvent(aEvent, atkObj);
+      {
+        AccTextChangeEvent* event = downcast_accEvent(aEvent);
+        NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
+
+        MAI_ATK_OBJECT(atkObj)-> FireTextChangeEvent(event->ModifiedText(),
+                                                     event->GetStartOffset(),
+                                                     event->GetLength(),
+                                                     event->IsTextInserted(),
+                                                     event->IsFromUserInput());
+
+        return NS_OK;
+      }
 
     case nsIAccessibleEvent::EVENT_FOCUS:
       {
         a11y::RootAccessible* rootAccWrap = accWrap->RootAccessible();
         if (rootAccWrap && rootAccWrap->mActivated) {
             atk_focus_tracker_notify(atkObj);
             // Fire state change event for focus
             atk_object_notify_state_change(atkObj, ATK_STATE_FOCUSED, true);
@@ -1450,65 +1461,63 @@ MaiAtkObject::FireStateChangeEvent(uint6
             // Fire state change for first state if there is one to map
             atk_object_notify_state_change(&parent,
                                            gAtkStateMap[stateIndex].atkState,
                                            aEnabled);
         }
     }
 }
 
+void
+a11y::ProxyTextChangeEvent(ProxyAccessible* aTarget, const nsString& aStr,
+                           int32_t aStart, uint32_t aLen, bool aIsInsert,
+                           bool aFromUser)
+{
+  MaiAtkObject* atkObj = MAI_ATK_OBJECT(GetWrapperFor(aTarget));
+  atkObj->FireTextChangeEvent(aStr, aStart, aLen, aIsInsert, aFromUser);
+}
+
 #define OLD_TEXT_INSERTED "text_changed::insert"
 #define OLD_TEXT_REMOVED "text_changed::delete"
 static const char* oldTextChangeStrings[2][2] = {
   { OLD_TEXT_REMOVED NON_USER_EVENT, OLD_TEXT_INSERTED NON_USER_EVENT },
   { OLD_TEXT_REMOVED, OLD_TEXT_INSERTED }
 };
 
 #define TEXT_INSERTED "text-insert"
 #define TEXT_REMOVED "text-remove"
 #define NON_USER_DETAIL "::system"
 static const char* textChangedStrings[2][2] = {
   { TEXT_REMOVED NON_USER_DETAIL, TEXT_INSERTED NON_USER_DETAIL },
   { TEXT_REMOVED, TEXT_INSERTED}
 };
 
-nsresult
-AccessibleWrap::FireAtkTextChangedEvent(AccEvent* aEvent,
-                                        AtkObject* aObject)
+void
+MaiAtkObject::FireTextChangeEvent(const nsString& aStr, int32_t aStart,
+                                  uint32_t aLen, bool aIsInsert,
+                                  bool aFromUser)
 {
-    AccTextChangeEvent* event = downcast_accEvent(aEvent);
-    NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
-
-    int32_t start = event->GetStartOffset();
-    uint32_t length = event->GetLength();
-    bool isInserted = event->IsTextInserted();
-    bool isFromUserInput = aEvent->IsFromUserInput();
-
   if (gAvailableAtkSignals == eUnknown)
     gAvailableAtkSignals =
-      g_signal_lookup("text-insert", G_OBJECT_TYPE(aObject)) ?
+      g_signal_lookup("text-insert", G_OBJECT_TYPE(this)) ?
         eHaveNewAtkTextSignals : eNoNewAtkSignals;
 
   if (gAvailableAtkSignals == eNoNewAtkSignals) {
     // XXX remove this code and the gHaveNewTextSignals check when we can
     // stop supporting old atk since it doesn't really work anyway
     // see bug 619002
     const char* signal_name =
-      oldTextChangeStrings[isFromUserInput][isInserted];
-    g_signal_emit_by_name(aObject, signal_name, start, length);
+      oldTextChangeStrings[aFromUser][aIsInsert];
+    g_signal_emit_by_name(this, signal_name, aStart, aLen);
   } else {
-    nsAutoString text;
-    event->GetModifiedText(text);
     const char* signal_name =
-      textChangedStrings[isFromUserInput][isInserted];
-    g_signal_emit_by_name(aObject, signal_name, start, length,
-                          NS_ConvertUTF16toUTF8(text).get());
+      textChangedStrings[aFromUser][aIsInsert];
+    g_signal_emit_by_name(this, signal_name, aStart, aLen,
+                          NS_ConvertUTF16toUTF8(aStr).get());
   }
-
-  return NS_OK;
 }
 
 #define ADD_EVENT "children_changed::add"
 #define HIDE_EVENT "children_changed::remove"
 
 static const char *kMutationStrings[2][2] = {
   { HIDE_EVENT NON_USER_EVENT, ADD_EVENT NON_USER_EVENT },
   { HIDE_EVENT, ADD_EVENT },
--- a/accessible/atk/AccessibleWrap.h
+++ b/accessible/atk/AccessibleWrap.h
@@ -74,29 +74,15 @@ protected:
   nsresult FireAtkStateChangeEvent(AccEvent* aEvent, AtkObject *aObject);
   nsresult FireAtkTextChangedEvent(AccEvent* aEvent, AtkObject *aObject);
   nsresult FireAtkShowHideEvent(AccEvent* aEvent, AtkObject *aObject,
                                 bool aIsAdded);
 
   AtkObject *mAtkObject;
 
 private:
-
-  /*
-   * do we have text-remove and text-insert signals if not we need to use
-   * text-changed see AccessibleWrap::FireAtkTextChangedEvent() and
-   * bug 619002
-   */
-  enum EAvailableAtkSignals {
-    eUnknown,
-    eHaveNewAtkTextSignals,
-    eNoNewAtkSignals
-  };
-
-  static EAvailableAtkSignals gAvailableAtkSignals;
-
   uint16_t CreateMaiInterfaces();
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif /* __NS_ACCESSIBLE_WRAP_H__ */
--- a/accessible/atk/nsMai.h
+++ b/accessible/atk/nsMai.h
@@ -76,11 +76,31 @@ struct MaiAtkObject
    * Shutdown this AtkObject.
    */
   void Shutdown();
 
   /*
    * Notify atk of a state change on this AtkObject.
    */
   void FireStateChangeEvent(uint64_t aState, bool aEnabled);
+
+  /*
+   * Notify ATK of a text change within this ATK object.
+   */
+  void FireTextChangeEvent(const nsString& aStr, int32_t aStart, uint32_t aLen,
+                           bool aIsInsert, bool aIsFromUser);
+
+private:
+  /*
+   * do we have text-remove and text-insert signals if not we need to use
+   * text-changed see AccessibleWrap::FireAtkTextChangedEvent() and
+   * bug 619002
+   */
+  enum EAvailableAtkSignals {
+    eUnknown,
+    eHaveNewAtkTextSignals,
+    eNoNewAtkSignals
+  };
+
+  static EAvailableAtkSignals gAvailableAtkSignals;
 };
 
 #endif /* __NS_MAI_H__ */
--- a/accessible/base/AccEvent.h
+++ b/accessible/base/AccEvent.h
@@ -189,16 +189,17 @@ public:
   }
 
   // AccTextChangeEvent
   int32_t GetStartOffset() const { return mStart; }
   uint32_t GetLength() const { return mModifiedText.Length(); }
   bool IsTextInserted() const { return mIsInserted; }
   void GetModifiedText(nsAString& aModifiedText)
     { aModifiedText = mModifiedText; }
+  const nsString& ModifiedText() const { return mModifiedText; }
 
 private:
   int32_t mStart;
   bool mIsInserted;
   nsString mModifiedText;
 
   friend class EventQueue;
   friend class AccReorderEvent;
--- a/accessible/base/DocManager.cpp
+++ b/accessible/base/DocManager.cpp
@@ -455,18 +455,24 @@ DocManager::CreateDocOrRootAccessible(ns
                              ApplicationAcc());
 
     if (IPCAccessibilityActive()) {
       DocAccessibleChild* ipcDoc = new DocAccessibleChild(docAcc);
       docAcc->SetIPCDoc(ipcDoc);
       nsIDocShell* docShell = aDocument->GetDocShell();
       if (docShell) {
         nsCOMPtr<nsITabChild> tabChild = do_GetInterface(docShell);
-        static_cast<TabChild*>(tabChild.get())->
-          SendPDocAccessibleConstructor(ipcDoc, nullptr, 0);
+
+        // XXX We may need to handle the case that we don't have a tab child
+        // differently.  It may be that this will cause us to fail to notify
+        // the parent process about important accessible documents.
+        if (tabChild) {
+          static_cast<TabChild*>(tabChild.get())->
+            SendPDocAccessibleConstructor(ipcDoc, nullptr, 0);
+        }
       }
     }
   } else {
     parentDocAcc->BindChildDocument(docAcc);
   }
 
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eDocCreate)) {
--- a/accessible/base/Platform.h
+++ b/accessible/base/Platform.h
@@ -1,16 +1,18 @@
 /* -*- 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 <stdint.h>
 
+class nsString;
+
 namespace mozilla {
 namespace a11y {
 
 class ProxyAccessible;
 
 enum EPlatformDisabledState {
   ePlatformIsForceEnabled = -1,
   ePlatformIsEnabled = 0,
@@ -65,11 +67,14 @@ void ProxyDestroyed(ProxyAccessible*);
 
 /**
  * Callied when an event is fired on a proxied accessible.
  */
 void ProxyEvent(ProxyAccessible* aTarget, uint32_t aEventType);
 void ProxyStateChangeEvent(ProxyAccessible* aTarget, uint64_t aState,
                            bool aEnabled);
 void ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset);
+void ProxyTextChangeEvent(ProxyAccessible* aTarget, const nsString& aStr,
+                          int32_t aStart, uint32_t aLen, bool aIsInsert,
+                          bool aFromUser);
 } // namespace a11y
 } // namespace mozilla
 
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -859,16 +859,26 @@ Accessible::HandleAccEvent(AccEvent* aEv
                                                                                   event->IsStateEnabled());
                                                      break;
                                                    }
       case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: {
                                                          AccCaretMoveEvent* event = downcast_accEvent(aEvent);
                                                          ipcDoc->SendEvent(id, event->GetCaretOffset());
                                                          break;
                                                        }
+      case nsIAccessibleEvent::EVENT_TEXT_INSERTED:
+      case nsIAccessibleEvent::EVENT_TEXT_REMOVED: {
+        AccTextChangeEvent* event = downcast_accEvent(aEvent);
+        ipcDoc->SendTextChangeEvent(id, event->ModifiedText(),
+                                    event->GetStartOffset(),
+                                    event->GetLength(),
+                                    event->IsTextInserted(),
+                                    event->IsFromUserInput());
+        break;
+                                                   }
       default:
                                                        ipcDoc->SendEvent(id, aEvent->GetEventType());
     }
   }
 
   nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
   NS_ENSURE_TRUE(obsService, NS_ERROR_FAILURE);
 
--- a/accessible/html/HTMLListAccessible.cpp
+++ b/accessible/html/HTMLListAccessible.cpp
@@ -97,29 +97,38 @@ HTMLLIAccessible::Bounds() const
 void
 HTMLLIAccessible::UpdateBullet(bool aHasBullet)
 {
   if (aHasBullet == !!mBullet) {
     NS_NOTREACHED("Bullet and accessible are in sync already!");
     return;
   }
 
+  nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this);
+  AutoTreeMutation mut(this);
+
   DocAccessible* document = Document();
   if (aHasBullet) {
     mBullet = new HTMLListBulletAccessible(mContent, mDoc);
     document->BindToDocument(mBullet, nullptr);
     InsertChildAt(0, mBullet);
+
+    nsRefPtr<AccShowEvent> event = new AccShowEvent(mBullet, mBullet->GetContent());
+    mDoc->FireDelayedEvent(event);
+    reorderEvent->AddSubMutationEvent(event);
   } else {
+    nsRefPtr<AccHideEvent> event = new AccHideEvent(mBullet, mBullet->GetContent());
+    mDoc->FireDelayedEvent(event);
+    reorderEvent->AddSubMutationEvent(event);
+
     RemoveChild(mBullet);
-    document->UnbindFromDocument(mBullet);
     mBullet = nullptr;
   }
 
-  // XXXtodo: fire show/hide and reorder events. That's hard to make it
-  // right now because coalescence happens by DOM node.
+  mDoc->FireDelayedEvent(reorderEvent);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLLIAccessible: Accessible protected
 
 void
 HTMLLIAccessible::CacheChildren()
 {
--- a/accessible/html/HTMLTableAccessible.cpp
+++ b/accessible/html/HTMLTableAccessible.cpp
@@ -368,18 +368,19 @@ HTMLTableRowAccessible::NativeRole()
   }
   return roles::ROW;
 }
 
 GroupPos
 HTMLTableRowAccessible::GroupPosition()
 {
   int32_t count = 0, index = 0;
-  if (nsCoreUtils::GetUIntAttr(nsAccUtils::TableFor(this)->GetContent(),
-                               nsGkAtoms::aria_rowcount, &count) &&
+  Accessible* table = nsAccUtils::TableFor(this);
+  if (table && nsCoreUtils::GetUIntAttr(table->GetContent(),
+                                        nsGkAtoms::aria_rowcount, &count) &&
       nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_rowindex, &index)) {
     return GroupPos(0, index, count);
   }
 
   return AccessibleWrap::GroupPosition();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -62,16 +62,21 @@ DocAccessibleParent::AddSubtree(ProxyAcc
   }
 
   const AccessibleData& newChild = aNewTree[aIdx];
   if (newChild.Role() > roles::LAST_ROLE) {
     NS_ERROR("invalid role");
     return 0;
   }
 
+  if (mAccessibles.Contains(newChild.ID())) {
+    NS_ERROR("ID already in use");
+    return 0;
+  }
+
   auto role = static_cast<a11y::role>(newChild.Role());
   ProxyAccessible* newProxy =
     new ProxyAccessible(newChild.ID(), aParent, this, role);
   aParent->AddChildAt(aIdxInParent, newProxy);
   mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
   ProxyCreated(newProxy, newChild.Interfaces());
 
   uint32_t accessibles = 1;
@@ -90,16 +95,18 @@ DocAccessibleParent::AddSubtree(ProxyAcc
 }
 
 bool
 DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID)
 {
   if (mShutdown)
     return true;
 
+  CheckDocTree();
+
   ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
   if (!rootEntry) {
     NS_ERROR("invalid root being removed!");
     return true;
   }
 
   ProxyAccessible* root = rootEntry->mProxy;
   if (!root) {
@@ -147,16 +154,33 @@ DocAccessibleParent::RecvCaretMoveEvent(
   if (!proxy)
     return false;
 
   ProxyCaretMoveEvent(proxy, aOffset);
   return true;
 }
 
 bool
+DocAccessibleParent::RecvTextChangeEvent(const uint64_t& aID,
+                                         const nsString& aStr,
+                                         const int32_t& aStart,
+                                         const uint32_t& aLen,
+                                         const bool& aIsInsert,
+                                         const bool& aFromUser)
+{
+  ProxyAccessible* target = GetAccessible(aID);
+  if (!target)
+  return false;
+
+  ProxyTextChangeEvent(target, aStr, aStart, aLen, aIsInsert, aFromUser);
+
+  return true;
+}
+
+bool
 DocAccessibleParent::RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID)
 {
   auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc);
   DebugOnly<bool> result = AddChildDoc(childDoc, aID, false);
   MOZ_ASSERT(result);
   return true;
 }
 
@@ -214,10 +238,22 @@ DocAccessibleParent::Destroy()
   mAccessibles.EnumerateEntries(ShutdownAccessibles, nullptr);
   ProxyDestroyed(this);
   if (mParentDoc)
     mParentDoc->RemoveChildDoc(this);
   else if (IsTopLevel())
     GetAccService()->RemoteDocShutdown(this);
 }
 
+void
+DocAccessibleParent::CheckDocTree() const
+{
+  size_t childDocs = mChildDocs.Length();
+  for (size_t i = 0; i < childDocs; i++) {
+    if (!mChildDocs[i] || mChildDocs[i]->mParentDoc != this)
+      MOZ_CRASH("document tree is broken!");
+
+    mChildDocs[i]->CheckDocTree();
+  }
+}
+
 } // a11y
 } // mozilla
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -50,16 +50,21 @@ public:
   virtual bool RecvHideEvent(const uint64_t& aRootID) override;
   virtual bool RecvStateChangeEvent(const uint64_t& aID,
                                     const uint64_t& aState,
                                     const bool& aEnabled) override final;
 
   virtual bool RecvCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset)
     override final;
 
+  virtual bool RecvTextChangeEvent(const uint64_t& aID, const nsString& aStr,
+                                   const int32_t& aStart, const uint32_t& aLen,
+                                   const bool& aIsInsert,
+                                   const bool& aFromUser) override;
+
   virtual bool RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID) override;
   void Unbind()
   {
     mParent = nullptr;
     ParentDoc()->mChildDocs.RemoveElement(this);
     mParentDoc = nullptr;
   }
 
@@ -140,16 +145,18 @@ private:
     enum { ALLOW_MEMMOVE = true };
 
     ProxyAccessible* mProxy;
   };
 
   uint32_t AddSubtree(ProxyAccessible* aParent,
                       const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
                       uint32_t aIdxInParent);
+  void CheckDocTree() const;
+
   static PLDHashOperator ShutdownAccessibles(ProxyEntry* entry, void* unused);
 
   nsTArray<DocAccessibleParent*> mChildDocs;
   DocAccessibleParent* mParentDoc;
 
   /*
    * Conceptually this is a map from IDs to proxies, but we store the ID in the
    * proxy object so we can't use a real map.
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/PDocAccessible.ipdl
@@ -54,16 +54,18 @@ parent:
    * Notify the parent process the document in the child process is firing an
    * event.
    */
   Event(uint64_t aID, uint32_t type);
   ShowEvent(ShowEventData data);
   HideEvent(uint64_t aRootID);
   StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled);
   CaretMoveEvent(uint64_t aID, int32_t aOffset);
+  TextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen,
+                  bool aIsInsert, bool aFromUser);
 
   /*
    * Tell the parent document to bind the existing document as a new child
    * document.
    */
   BindChildDoc(PDocAccessible aChildDoc, uint64_t aID);
 
 child:
--- a/accessible/mac/Platform.mm
+++ b/accessible/mac/Platform.mm
@@ -78,16 +78,21 @@ ProxyStateChangeEvent(ProxyAccessible* a
 void
 ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset)
 {
   mozAccessible* wrapper = GetNativeFromProxy(aTarget);
   if (wrapper)
     [wrapper selectedTextDidChange];
 }
 
+void
+ProxyTextChangeEvent(ProxyAccessible*, const nsString&, int32_t, uint32_t,
+                     bool, bool)
+{
+}
 } // namespace a11y
 } // namespace mozilla
 
 @interface GeckoNSApplication(a11y)
 -(void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute;
 @end
 
 @implementation GeckoNSApplication(a11y)
--- a/accessible/mac/mozAccessible.h
+++ b/accessible/mac/mozAccessible.h
@@ -13,36 +13,50 @@
 @class mozRootAccessible;
 
 /**
  * All mozAccessibles are either abstract objects (that correspond to XUL
  * widgets, HTML frames, etc) or are attached to a certain view; for example
  * a document view. When we hand an object off to an AT, we always want
  * to give it the represented view, in the latter case.
  */
+
+namespace mozilla {
+namespace a11y {
+
 inline id <mozAccessible>
 GetObjectOrRepresentedView(id <mozAccessible> aObject)
 {
   return [aObject hasRepresentedView] ? [aObject representedView] : aObject;
 }
 
 inline mozAccessible*
-GetNativeFromGeckoAccessible(mozilla::a11y::Accessible* aAccessible)
+GetNativeFromGeckoAccessible(Accessible* aAccessible)
 {
   mozAccessible* native = nil;
   aAccessible->GetNativeInterface((void**)&native);
   return native;
 }
 
 inline mozAccessible*
-GetNativeFromProxy(mozilla::a11y::ProxyAccessible* aProxy)
+GetNativeFromProxy(const ProxyAccessible* aProxy)
 {
   return reinterpret_cast<mozAccessible*>(aProxy->GetWrapper());
 }
 
+ProxyAccessible* GetProxyUnignoredParent(const ProxyAccessible* aProxy);
+
+void GetProxyUnignoredChildren(const ProxyAccessible* aProxy,
+                               nsTArray<ProxyAccessible*>* aChildrenArray);
+
+BOOL IsProxyIgnored(const ProxyAccessible* aProxy);
+
+} // a11y
+} // mozilla
+
 // This is OR'd with the Accessible owner to indicate the wrap-ee is a proxy.
 static const uintptr_t IS_PROXY = 1;
 
 @interface mozAccessible : NSObject <mozAccessible>
 {
   /**
    * Weak reference; it owns us.
    */
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -71,34 +71,93 @@ GetClosestInterestingAccessible(id anObj
   if ([unignoredObject respondsToSelector:@selector(hasRepresentedView)])
     return GetObjectOrRepresentedView(unignoredObject);
 
   return unignoredObject;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
+ProxyAccessible*
+a11y::GetProxyUnignoredParent(const ProxyAccessible* aProxy)
+{
+  ProxyAccessible* parent = aProxy->Parent();
+  while (parent && IsProxyIgnored(aProxy))
+    parent = parent->Parent();
+
+  return parent;
+}
+
+void
+a11y::GetProxyUnignoredChildren(const ProxyAccessible* aProxy,
+                                nsTArray<ProxyAccessible*>* aChildrenArray)
+{
+  if (aProxy->MustPruneChildren())
+    return;
+
+  uint32_t childCount = aProxy->ChildrenCount();
+  for (size_t childIdx = 0; childIdx < childCount; childIdx++) {
+    ProxyAccessible* childProxy = aProxy->ChildAt(childIdx);
+
+    // If element is ignored, then add its children as substitutes.
+    if (IsProxyIgnored(childProxy)) {
+      GetProxyUnignoredChildren(aProxy, aChildrenArray);
+      continue;
+    }
+
+    aChildrenArray->AppendElement(childProxy);
+  }
+}
+
+BOOL
+a11y::IsProxyIgnored(const ProxyAccessible* aProxy)
+{
+  mozAccessible* nativeObject = GetNativeFromProxy(aProxy);
+  if (!nativeObject)
+   return true;
+
+  return [nativeObject accessibilityIsIgnored];
+}
+
 // convert an array of Gecko accessibles to an NSArray of native accessibles
 static inline NSMutableArray*
 ConvertToNSArray(nsTArray<Accessible*>& aArray)
 {
   NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
 
   // iterate through the list, and get each native accessible.
-  uint32_t totalCount = aArray.Length();
-  for (uint32_t i = 0; i < totalCount; i++) {
+  size_t totalCount = aArray.Length();
+  for (size_t i = 0; i < totalCount; i++) {
     Accessible* curAccessible = aArray.ElementAt(i);
     mozAccessible* curNative = GetNativeFromGeckoAccessible(curAccessible);
     if (curNative)
       [nativeArray addObject:GetObjectOrRepresentedView(curNative)];
   }
 
   return nativeArray;
 }
 
+// convert an array of Gecko proxy accessibles to an NSArray of native accessibles
+static inline NSMutableArray*
+ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
+{
+  NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
+
+  // iterate through the list, and get each native accessible.
+  size_t totalCount = aArray.Length();
+  for (size_t i = 0; i < totalCount; i++) {
+    ProxyAccessible* curAccessible = aArray.ElementAt(i);
+    mozAccessible* curNative = GetNativeFromProxy(curAccessible);
+    if (curNative)
+      [nativeArray addObject:GetObjectOrRepresentedView(curNative)];
+  }
+
+  return nativeArray;
+}
+
 #pragma mark -
 
 @implementation mozAccessible
 
 - (id)initWithAccessible:(uintptr_t)aGeckoAccessible
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
@@ -607,31 +666,44 @@ ConvertToNSArray(nsTArray<Accessible*>& 
 }
 
 #pragma mark -
 
 - (id <mozAccessible>)parent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
-  AccessibleWrap* accWrap = [self getGeckoAccessible];
-  Accessible* accessibleParent = accWrap->GetUnignoredParent();
-  if (accessibleParent) {
-    id nativeParent = GetNativeFromGeckoAccessible(accessibleParent);
+  id nativeParent = nil;
+  if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+    Accessible* accessibleParent = accWrap->GetUnignoredParent();
+    if (accessibleParent)
+      nativeParent = GetNativeFromGeckoAccessible(accessibleParent);
     if (nativeParent)
       return GetClosestInterestingAccessible(nativeParent);
+    // GetUnignoredParent() returns null when there is no unignored accessible all the way up to
+    // the root accessible. so we'll have to return whatever native accessible is above our root accessible
+    // (which might be the owning NSWindow in the application, for example).
+    //
+    // get the native root accessible, and tell it to return its first parent unignored accessible.
+    nativeParent = GetNativeFromGeckoAccessible(accWrap->RootAccessible());
+  } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+    // Go up the chain to find a parent that is not ignored.
+    ProxyAccessible* accessibleParent = GetProxyUnignoredParent(proxy);
+    if (accessibleParent)
+      nativeParent = GetNativeFromProxy(accessibleParent);
+    if (nativeParent)
+      return GetClosestInterestingAccessible(nativeParent);
+
+    Accessible* outerDoc = proxy->OuterDocOfRemoteBrowser();
+    nativeParent = outerDoc ?
+      GetNativeFromGeckoAccessible(outerDoc->RootAccessible()) : nil;
+  } else {
+    return nil;
   }
 
-  // GetUnignoredParent() returns null when there is no unignored accessible all the way up to
-  // the root accessible. so we'll have to return whatever native accessible is above our root accessible
-  // (which might be the owning NSWindow in the application, for example).
-  //
-  // get the native root accessible, and tell it to return its first parent unignored accessible.
-  id nativeParent =
-    GetNativeFromGeckoAccessible(accWrap->RootAccessible());
   NSAssert1 (nativeParent, @"!!! we can't find a parent for %@", self);
 
   return GetClosestInterestingAccessible(nativeParent);
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (BOOL)hasRepresentedView
@@ -651,23 +723,29 @@ ConvertToNSArray(nsTArray<Accessible*>& 
 
 // gets our native children lazily.
 // returns nil when there are no children.
 - (NSArray*)children
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   AccessibleWrap* accWrap = [self getGeckoAccessible];
-  if (mChildren || !accWrap->AreChildrenCached())
+  if (mChildren || (accWrap && !accWrap->AreChildrenCached()))
     return mChildren;
 
   // get the array of children.
-  nsAutoTArray<Accessible*, 10> childrenArray;
-  accWrap->GetUnignoredChildren(&childrenArray);
-  mChildren = ConvertToNSArray(childrenArray);
+  if (accWrap) {
+    nsAutoTArray<Accessible*, 10> childrenArray;
+    accWrap->GetUnignoredChildren(&childrenArray);
+    mChildren = ConvertToNSArray(childrenArray);
+  } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+    nsAutoTArray<ProxyAccessible*, 10> childrenArray;
+    GetProxyUnignoredChildren(proxy, &childrenArray);
+    mChildren = ConvertToNSArray(childrenArray);
+  }
 
 #ifdef DEBUG_hakan
   // make sure we're not returning any ignored accessibles.
   NSEnumerator *e = [mChildren objectEnumerator];
   mozAccessible *m = nil;
   while ((m = [e nextObject])) {
     NSAssert1(![m accessibilityIsIgnored], @"we should never return an ignored accessible! (%@)", m);
   }
--- a/accessible/other/Platform.cpp
+++ b/accessible/other/Platform.cpp
@@ -38,8 +38,14 @@ void
 a11y::ProxyStateChangeEvent(ProxyAccessible*, uint64_t, bool)
 {
 }
 
 void
 a11y::ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset)
 {
 }
+
+void
+a11y::ProxyTextChangeEvent(ProxyAccessible*, const nsString&, int32_t, uint32_t,
+                     bool, bool)
+{
+}
--- a/accessible/tests/mochitest/treeupdate/a11y.ini
+++ b/accessible/tests/mochitest/treeupdate/a11y.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 
 [test_ariadialog.html]
 [test_bug852150.xhtml]
 [test_bug883708.xhtml]
 [test_bug884251.xhtml]
 [test_bug895082.html]
 [test_bug1040735.html]
+[test_bug1100602.html]
 [test_canvas.html]
 [test_colorpicker.xul]
 [test_contextmenu.xul]
 [test_cssoverflow.html]
 [test_deck.xul]
 [test_doc.html]
 [test_gencontent.html]
 [test_hidden.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug1100602.html
@@ -0,0 +1,114 @@
+<html>
+
+<head>
+  <title>Test hide/show events for HTMLListBulletAccessibles on list restyle</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"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+    /**
+     * Change list style type to none.
+     */
+    function hideBullet()
+    {
+      this.eventSeq = [];
+      this.liAcc = getAccessible("list_element");
+      this.bullet = this.liAcc.firstChild;
+
+      this.eventSeq.push(new invokerChecker(EVENT_HIDE, this.bullet));
+      this.eventSeq.push(new invokerChecker(EVENT_REORDER, this.liAcc));
+
+      this.invoke = function hideBullet_invoke()
+      {
+        getNode("list").setAttribute("style", "list-style-type: none;");
+      }
+
+      this.finalCheck = function hideBullet_finalCheck()
+      {
+        is(this.liAcc.name, "list element",
+          "Check that first child of LI is not a bullet.");
+      }
+
+      this.getID = function hideBullet_getID()
+      {
+        return "Hide bullet by setting style to none";
+      }
+    }
+
+    /**
+     * Change list style type to circles.
+     */
+    function showBullet()
+    {
+      this.eventSeq = [];
+      this.liAcc = getAccessible("list_element");
+
+      this.eventSeq.push(new invokerChecker(EVENT_SHOW,
+                                            function(aNode) { return aNode.firstChild; },
+                                            this.liAcc));
+      this.eventSeq.push(new invokerChecker(EVENT_REORDER, this.liAcc));
+
+      this.invoke = function showBullet_invoke()
+      {
+        getNode("list").setAttribute("style", "list-style-type: circle;");
+      }
+
+      this.finalCheck = function showBullet_finalCheck()
+      {
+        is(this.liAcc.name, "◦ list element",
+          "Check that first child of LI is a circle bullet.");
+      }
+
+      this.getID = function showBullet_getID()
+      {
+        return "Show bullet by setting style to circle";
+      }
+    }
+
+    var gQueue = null;
+    function doTest()
+    {
+
+      var list = getNode("list");
+      list.setAttribute("style", "list-style-type: circle;");
+
+      gQueue = new eventQueue();
+      gQueue.push(new hideBullet());
+      gQueue.push(new showBullet());
+      gQueue.invoke(); // SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1100602"
+     title="[e10s] crash in mozilla::a11y::ProxyAccessible::Shutdown()">
+    Mozilla Bug 1100602
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <ol id="list">
+    <li id="list_element">list element</li>
+  </ol>
+
+</body>
+</html>
--- a/accessible/windows/msaa/ApplicationAccessibleWrap.cpp
+++ b/accessible/windows/msaa/ApplicationAccessibleWrap.cpp
@@ -8,32 +8,33 @@
 #include "ApplicationAccessibleWrap.h"
 
 #include "AccessibleApplication_i.c"
 #include "IUnknownImpl.h"
 
 #include "nsIGfxInfo.h"
 #include "nsIPersistentProperties2.h"
 #include "nsServiceManagerUtils.h"
+#include "mozilla/Services.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 NS_IMPL_ISUPPORTS_INHERITED0(ApplicationAccessibleWrap,
                              ApplicationAccessible)
 
 already_AddRefed<nsIPersistentProperties>
 ApplicationAccessibleWrap::NativeAttributes()
 {
   nsCOMPtr<nsIPersistentProperties> attributes =
     do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
 
-  nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+  nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   if (gfxInfo) {
     bool isD2DEnabled = false;
     gfxInfo->GetD2DEnabled(&isD2DEnabled);
     nsAutoString unused;
     attributes->SetStringProperty(
       NS_LITERAL_CSTRING("D2D"),
       isD2DEnabled ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false"),
         unused);
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -63,8 +63,14 @@ void
 a11y::ProxyStateChangeEvent(ProxyAccessible*, uint64_t, bool)
 {
 }
 
 void
 a11y::ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset)
 {
 }
+
+void
+a11y::ProxyTextChangeEvent(ProxyAccessible*, const nsString&, int32_t, uint32_t,
+                     bool, bool)
+{
+}
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -328,17 +328,16 @@ pref("media.eme.enabled", true);
 pref("media.eme.apiVisible", true);
 
 // The default number of decoded video frames that are enqueued in
 // MediaDecoderReader's mVideoQueue.
 pref("media.video-queue.default-size", 3);
 
 // optimize images' memory usage
 pref("image.downscale-during-decode.enabled", true);
-pref("image.decode-only-on-draw.enabled", false);
 pref("image.mem.allow_locking_in_content_processes", true);
 pref("image.decode.retry-on-alloc-failure", true);
 // Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
 // Almost everything that was factored into 'max_decoded_image_kb' is now stored
 // in the surface cache.  1/8 of main memory is 32MB on a 256MB device, which is
 // about the same as the old 'max_decoded_image_kb'.
 pref("image.mem.surfacecache.max_size_kb", 131072);  // 128MB
 pref("image.mem.surfacecache.size_factor", 8);  // 1/8 of main memory
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="66638d0e65bf58b7f640bcc7bed4a0b23d1356c6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="42b31cf3248acf7acd1fd30c7491871c50305fa8"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
@@ -120,17 +120,17 @@
   <project name="platform/external/icu4c" path="external/icu4c" revision="d3ec7428eb276db43b7ed0544e09344a6014806c"/>
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="76c4bf4bc430a1b8317f2f21ef735867733e50cc"/>
   <project name="platform/system/media" path="system/media" revision="c1332c21c608f4932a6d7e83450411cde53315ef"/>
   <default remote="caf" revision="LNX.LA.3.5.2.1.1" sync-j="4"/>
   <!-- Platform common things -->
   <project name="device-shinano-common" path="device/sony/shinano-common" remote="b2g" revision="e9ef670a15d56ea312e70d4b11c4aaeac404f9d2"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="1bb28abbc215f45220620af5cd60a8ac1be93722"/>
   <project name="device/qcom/common" path="device/qcom/common" revision="2501e5940ba69ece7654ff85611c76ae5bda299c"/>
-  <project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="5ada05ac150f643ef19e87015df7e106b88effe7"/>
+  <project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="d620691cad7aee780018e98159ff03bf99840317"/>
   <project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="d61fc97258c8b0c362430dd2eb195dcc4d266f14"/>
   <project name="init_sh" path="external/init_sh" remote="b2g" revision="3bdd26e092db9c47c5beb04b4809a35f8f767b8a"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="0a01977f34d6e86fe23d6c0ec75e96ba988bbebb"/>
   <project name="platform_external_libnfc-pn547" path="external/libnfc-pn547" remote="b2g" revision="5bb999b84b8adc14f6bea004d523ba258dea8188"/>
   <project name="timekeep" path="external/timekeep" remote="b2g" revision="58e9fd70a62d2c0ebf9bc2bd99439dcc5be8cad8"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="5b71e40213f650459e95d35b6f14af7e88d8ab62"/>
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="a920312eb6299b6cc11f7136254c4b0ba7a663be"/>
   <project name="platform/frameworks/base" path="frameworks/base" revision="da8e6bc53c8bc669da0bb627904d08aa293f2497"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="66638d0e65bf58b7f640bcc7bed4a0b23d1356c6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="42b31cf3248acf7acd1fd30c7491871c50305fa8"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="83760d213fb3bec7b4117d266fcfbf6fe2ba14ab"/>
   <project name="device/common" path="device/common" revision="6a2995683de147791e516aae2ccb31fdfbe2ad30"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,20 +14,20 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="66638d0e65bf58b7f640bcc7bed4a0b23d1356c6"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
-  <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>
+  <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="66638d0e65bf58b7f640bcc7bed4a0b23d1356c6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="42b31cf3248acf7acd1fd30c7491871c50305fa8"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="66638d0e65bf58b7f640bcc7bed4a0b23d1356c6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="42b31cf3248acf7acd1fd30c7491871c50305fa8"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="66638d0e65bf58b7f640bcc7bed4a0b23d1356c6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="42b31cf3248acf7acd1fd30c7491871c50305fa8"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="8af5ff6f5dced9eb5a8127459df6c75d24342204"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="30915518fa7ea07166efedc191a4f40aef516fe7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="96eee58e3389fb05a835310d6a06a6ba4486097a"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="7c8a46698171aa2e0be09edb43d15a6acf832770"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="24b2038be8a636fd4a5d21f0abae1e466b07bcf7"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,20 +14,20 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="66638d0e65bf58b7f640bcc7bed4a0b23d1356c6"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
-  <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>
+  <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="66638d0e65bf58b7f640bcc7bed4a0b23d1356c6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="42b31cf3248acf7acd1fd30c7491871c50305fa8"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "66638d0e65bf58b7f640bcc7bed4a0b23d1356c6", 
+        "git_revision": "77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "f99acf8fe79c200c9456000e8651e48be96a8bc0", 
+    "revision": "dfd4a9e8f31e64d427030d3612a48f7dbcada5d3", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="66638d0e65bf58b7f640bcc7bed4a0b23d1356c6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="42b31cf3248acf7acd1fd30c7491871c50305fa8"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="66638d0e65bf58b7f640bcc7bed4a0b23d1356c6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="42b31cf3248acf7acd1fd30c7491871c50305fa8"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="8af5ff6f5dced9eb5a8127459df6c75d24342204"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="30915518fa7ea07166efedc191a4f40aef516fe7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="96eee58e3389fb05a835310d6a06a6ba4486097a"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="7c8a46698171aa2e0be09edb43d15a6acf832770"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="24b2038be8a636fd4a5d21f0abae1e466b07bcf7"/>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -266,17 +266,16 @@ pref("general.autoScroll", false);
 #else
 pref("general.autoScroll", true);
 #endif
 
 // At startup, check if we're the default browser and prompt user if not.
 pref("browser.shell.checkDefaultBrowser", true);
 pref("browser.shell.shortcutFavicons",true);
 pref("browser.shell.mostRecentDateSetAsDefault", "");
-pref("browser.shell.windows10DefaultBrowserABTest", -1);
 
 // 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session
 // The behavior of option 3 is detailed at: http://wiki.mozilla.org/Session_Restore
 pref("browser.startup.page",                1);
 pref("browser.startup.homepage",            "chrome://branding/locale/browserconfig.properties");
 
 pref("browser.slowStartup.notificationDisabled", false);
 pref("browser.slowStartup.timeThreshold", 40000);
@@ -1452,18 +1451,18 @@ pref("devtools.debugger.chrome-debugging
 pref("devtools.debugger.remote-host", "localhost");
 pref("devtools.debugger.remote-timeout", 20000);
 pref("devtools.debugger.pause-on-exceptions", false);
 pref("devtools.debugger.ignore-caught-exceptions", true);
 pref("devtools.debugger.source-maps-enabled", true);
 pref("devtools.debugger.pretty-print-enabled", true);
 pref("devtools.debugger.auto-pretty-print", false);
 pref("devtools.debugger.auto-black-box", true);
-pref("devtools.debugger.tracer", false);
 pref("devtools.debugger.workers", false);
+pref("devtools.debugger.promise", false);
 
 // The default Debugger UI settings
 pref("devtools.debugger.ui.panes-workers-and-sources-width", 200);
 pref("devtools.debugger.ui.panes-instruments-width", 300);
 pref("devtools.debugger.ui.panes-visible-on-startup", false);
 pref("devtools.debugger.ui.variables-sorting-enabled", true);
 pref("devtools.debugger.ui.variables-only-enum-visible", false);
 pref("devtools.debugger.ui.variables-searchbox-visible", false);
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -392,92 +392,108 @@ const gXPInstallObserver = {
   _removeProgressNotification(aBrowser) {
     let notification = PopupNotifications.getNotification("addon-progress", aBrowser);
     if (notification)
       notification.remove();
   }
 };
 
 var LightWeightThemeWebInstaller = {
+  init: function () {
+    let mm = window.messageManager;
+    mm.addMessageListener("LightWeightThemeWebInstaller:Install", this);
+    mm.addMessageListener("LightWeightThemeWebInstaller:Preview", this);
+    mm.addMessageListener("LightWeightThemeWebInstaller:ResetPreview", this);
+  },
+
+  receiveMessage: function (message) {
+    // ignore requests from background tabs
+    if (message.target != gBrowser.selectedBrowser) {
+      return;
+    }
+
+    let data = message.data;
+
+    switch (message.name) {
+      case "LightWeightThemeWebInstaller:Install": {
+        this._installRequest(data.themeData, data.baseURI);
+        break;
+      }
+      case "LightWeightThemeWebInstaller:Preview": {
+        this._preview(data.themeData, data.baseURI);
+        break;
+      }
+      case "LightWeightThemeWebInstaller:ResetPreview": {
+        this._resetPreview(data && data.baseURI);
+        break;
+      }
+    }
+  },
+
   handleEvent: function (event) {
     switch (event.type) {
-      case "InstallBrowserTheme":
-      case "PreviewBrowserTheme":
-      case "ResetBrowserThemePreview":
-        // ignore requests from background tabs
-        if (event.target.ownerDocument.defaultView.top != content)
-          return;
-    }
-    switch (event.type) {
-      case "InstallBrowserTheme":
-        this._installRequest(event);
-        break;
-      case "PreviewBrowserTheme":
-        this._preview(event);
-        break;
-      case "ResetBrowserThemePreview":
-        this._resetPreview(event);
-        break;
-      case "pagehide":
-      case "TabSelect":
+      case "TabSelect": {
         this._resetPreview();
         break;
+      }
     }
   },
 
   get _manager () {
-    var temp = {};
+    let temp = {};
     Cu.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
     delete this._manager;
     return this._manager = temp.LightweightThemeManager;
   },
 
-  _installRequest: function (event) {
-    var node = event.target;
-    var data = this._getThemeFromNode(node);
-    if (!data)
+  _installRequest: function (dataString, baseURI) {
+    let data = this._manager.parseTheme(dataString, baseURI);
+
+    if (!data) {
       return;
+    }
 
-    if (this._isAllowed(node)) {
+    if (this._isAllowed(baseURI)) {
       this._install(data);
       return;
     }
 
-    var allowButtonText =
+    let allowButtonText =
       gNavigatorBundle.getString("lwthemeInstallRequest.allowButton");
-    var allowButtonAccesskey =
+    let allowButtonAccesskey =
       gNavigatorBundle.getString("lwthemeInstallRequest.allowButton.accesskey");
-    var message =
+    let message =
       gNavigatorBundle.getFormattedString("lwthemeInstallRequest.message",
-                                          [node.ownerDocument.location.host]);
-    var buttons = [{
+                                          [makeURI(baseURI).host]);
+    let buttons = [{
       label: allowButtonText,
       accessKey: allowButtonAccesskey,
       callback: function () {
         LightWeightThemeWebInstaller._install(data);
       }
     }];
 
     this._removePreviousNotifications();
 
-    var notificationBox = gBrowser.getNotificationBox();
-    var notificationBar =
+    let notificationBox = gBrowser.getNotificationBox();
+    let notificationBar =
       notificationBox.appendNotification(message, "lwtheme-install-request", "",
                                          notificationBox.PRIORITY_INFO_MEDIUM,
                                          buttons);
     notificationBar.persistence = 1;
   },
 
   _install: function (newLWTheme) {
-    var previousLWTheme = this._manager.currentTheme;
+    let previousLWTheme = this._manager.currentTheme;
 
-    var listener = {
+    let listener = {
       onEnabling: function(aAddon, aRequiresRestart) {
-        if (!aRequiresRestart)
+        if (!aRequiresRestart) {
           return;
+        }
 
         let messageString = gNavigatorBundle.getFormattedString("lwthemeNeedsRestart.message",
           [aAddon.name], 1);
 
         let action = {
           label: gNavigatorBundle.getString("lwthemeNeedsRestart.button"),
           accessKey: gNavigatorBundle.getString("lwthemeNeedsRestart.accesskey"),
           callback: function () {
@@ -504,100 +520,92 @@ var LightWeightThemeWebInstaller = {
     AddonManager.removeAddonListener(listener);
   },
 
   _postInstallNotification: function (newTheme, previousTheme) {
     function text(id) {
       return gNavigatorBundle.getString("lwthemePostInstallNotification." + id);
     }
 
-    var buttons = [{
+    let buttons = [{
       label: text("undoButton"),
       accessKey: text("undoButton.accesskey"),
       callback: function () {
         LightWeightThemeWebInstaller._manager.forgetUsedTheme(newTheme.id);
         LightWeightThemeWebInstaller._manager.currentTheme = previousTheme;
       }
     }, {
       label: text("manageButton"),
       accessKey: text("manageButton.accesskey"),
       callback: function () {
         BrowserOpenAddonsMgr("addons://list/theme");
       }
     }];
 
     this._removePreviousNotifications();
 
-    var notificationBox = gBrowser.getNotificationBox();
-    var notificationBar =
+    let notificationBox = gBrowser.getNotificationBox();
+    let notificationBar =
       notificationBox.appendNotification(text("message"),
                                          "lwtheme-install-notification", "",
                                          notificationBox.PRIORITY_INFO_MEDIUM,
                                          buttons);
     notificationBar.persistence = 1;
     notificationBar.timeout = Date.now() + 20000; // 20 seconds
   },
 
   _removePreviousNotifications: function () {
-    var box = gBrowser.getNotificationBox();
+    let box = gBrowser.getNotificationBox();
 
     ["lwtheme-install-request",
      "lwtheme-install-notification"].forEach(function (value) {
-        var notification = box.getNotificationWithValue(value);
+        let notification = box.getNotificationWithValue(value);
         if (notification)
           box.removeNotification(notification);
       });
   },
 
-  _previewWindow: null,
-  _preview: function (event) {
-    if (!this._isAllowed(event.target))
+  _preview: function (dataString, baseURI) {
+    if (!this._isAllowed(baseURI))
       return;
 
-    var data = this._getThemeFromNode(event.target);
+    let data = this._manager.parseTheme(dataString, baseURI);
     if (!data)
       return;
 
     this._resetPreview();
-
-    this._previewWindow = event.target.ownerDocument.defaultView;
-    this._previewWindow.addEventListener("pagehide", this, true);
     gBrowser.tabContainer.addEventListener("TabSelect", this, false);
-
     this._manager.previewTheme(data);
   },
 
-  _resetPreview: function (event) {
-    if (!this._previewWindow ||
-        event && !this._isAllowed(event.target))
+  _resetPreview: function (baseURI) {
+    if (baseURI && !this._isAllowed(baseURI))
       return;
-
-    this._previewWindow.removeEventListener("pagehide", this, true);
-    this._previewWindow = null;
     gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
-
     this._manager.resetPreview();
   },
 
-  _isAllowed: function (node) {
-    var pm = Services.perms;
-
-    var uri = node.ownerDocument.documentURIObject;
-
-    if (!uri.schemeIs("https"))
+  _isAllowed: function (srcURIString) {
+    let uri;
+    try {
+      uri = makeURI(srcURIString);
+    }
+    catch(e) {
+      //makeURI fails if srcURIString is a nonsense URI
       return false;
+    }
 
-    return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
-  },
+    if (!uri.schemeIs("https")) {
+      return false;
+    }
 
-  _getThemeFromNode: function (node) {
-    return this._manager.parseTheme(node.getAttribute("data-browsertheme"),
-                                    node.baseURI);
+    let pm = Services.perms;
+    return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
   }
-}
+};
 
 /*
  * Listen for Lightweight Theme styling changes and update the browser's theme accordingly.
  */
 let LightweightThemeListener = {
   _modifiedStyles: [],
 
   init: function () {
--- a/browser/base/content/browser-trackingprotection.js
+++ b/browser/base/content/browser-trackingprotection.js
@@ -7,16 +7,17 @@ let TrackingProtection = {
   PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled",
   enabledGlobally: false,
   enabledInPrivateWindows: false,
 
   init() {
     let $ = selector => document.querySelector(selector);
     this.container = $("#tracking-protection-container");
     this.content = $("#tracking-protection-content");
+    this.icon = $("#tracking-protection-icon");
 
     this.updateEnabled();
     Services.prefs.addObserver(this.PREF_ENABLED_GLOBALLY, this, false);
     Services.prefs.addObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this, false);
 
     this.enabledHistogram.add(this.enabledGlobally);
   },
 
@@ -55,25 +56,24 @@ let TrackingProtection = {
     if (!this.enabled) {
       return;
     }
 
     let {
       STATE_BLOCKED_TRACKING_CONTENT, STATE_LOADED_TRACKING_CONTENT
     } = Ci.nsIWebProgressListener;
 
-    if (state & STATE_BLOCKED_TRACKING_CONTENT) {
-      this.content.setAttribute("block-active", true);
-      this.content.removeAttribute("block-disabled");
-    } else if (state & STATE_LOADED_TRACKING_CONTENT) {
-      this.content.setAttribute("block-disabled", true);
-      this.content.removeAttribute("block-active");
-    } else {
-      this.content.removeAttribute("block-disabled");
-      this.content.removeAttribute("block-active");
+    for (let element of [this.icon, this.content]) {
+      if (state & STATE_BLOCKED_TRACKING_CONTENT) {
+        element.setAttribute("state", "blocked-tracking-content");
+      } else if (state & STATE_LOADED_TRACKING_CONTENT) {
+        element.setAttribute("state", "loaded-tracking-content");
+      } else {
+        element.removeAttribute("state");
+      }
     }
 
     // Telemetry for state change.
     this.eventsHistogram.add(0);
   },
 
   disableForCurrentPage() {
     // Convert document URI into the format used by
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1367,19 +1367,17 @@ var gBrowserInit = {
 
 #ifndef XP_MACOSX
     updateEditUIVisibility();
     let placesContext = document.getElementById("placesContext");
     placesContext.addEventListener("popupshowing", updateEditUIVisibility, false);
     placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
 #endif
 
-    gBrowser.mPanelContainer.addEventListener("InstallBrowserTheme", LightWeightThemeWebInstaller, false, true);
-    gBrowser.mPanelContainer.addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstaller, false, true);
-    gBrowser.mPanelContainer.addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstaller, false, true);
+    LightWeightThemeWebInstaller.init();
 
     if (Win7Features)
       Win7Features.onOpenWindow();
 
     FullScreen.init();
 
 #ifdef MOZ_SERVICES_SYNC
     // initialize the sync UI
@@ -6637,16 +6635,21 @@ var gIdentityHandler = {
     return this._identityPopupSecurityContent =
       document.getElementById("identity-popup-security-content");
   },
   get _identityPopupSecurityView () {
     delete this._identityPopupSecurityView;
     return this._identityPopupSecurityView =
       document.getElementById("identity-popup-securityView");
   },
+  get _identityPopupMainView () {
+    delete this._identityPopupMainView;
+    return this._identityPopupMainView =
+      document.getElementById("identity-popup-mainView");
+  },
   get _identityIconLabel () {
     delete this._identityIconLabel;
     return this._identityIconLabel = document.getElementById("identity-icon-label");
   },
   get _overrideService () {
     delete this._overrideService;
     return this._overrideService = Cc["@mozilla.org/security/certoverride;1"]
                                      .getService(Ci.nsICertOverrideService);
@@ -6982,16 +6985,17 @@ var gIdentityHandler = {
    * based on the specified mode, and the details of the SSL cert, where
    * applicable
    *
    * @param newMode The newly set identity mode.  Should be one of the IDENTITY_MODE_* constants.
    */
   setPopupMessages : function(newMode) {
 
     this._identityPopup.className = newMode;
+    this._identityPopupMainView.className = newMode;
     this._identityPopupSecurityView.className = newMode;
     this._identityPopupSecurityContent.className = newMode;
 
     // Initialize the optional strings to empty values
     let supplemental = "";
     let verifier = "";
     let host = "";
     let owner = "";
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -757,16 +757,17 @@
                    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);"
                    onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
                    ondragstart="gIdentityHandler.onDragStart(event);">
+                <image id="tracking-protection-icon"/>
                 <image id="page-proxy-favicon"
                        consumeanchor="identity-box"
                        onclick="PageProxyClickHandler(event);"
                        pageproxystate="invalid"/>
                 <hbox id="identity-icon-labels">
                   <label id="identity-icon-label" class="plain" flex="1"/>
                   <label id="identity-icon-country-label" class="plain"/>
                 </hbox>
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -712,16 +712,67 @@ addMessageListener("ContextMenu:SearchFi
     let separator = spec.includes("?") ? "&" : "?";
     spec += separator + formData.join("&");
   }
 
   sendAsyncMessage("ContextMenu:SearchFieldBookmarkData:Result",
                    { spec, title, description, postData, charset });
 });
 
+let LightWeightThemeWebInstallListener = {
+  _previewWindow: null,
+
+  init: function() {
+    addEventListener("InstallBrowserTheme", this, false, true);
+    addEventListener("PreviewBrowserTheme", this, false, true);
+    addEventListener("ResetBrowserThemePreview", this, false, true);
+  },
+
+  handleEvent: function (event) {
+    switch (event.type) {
+      case "InstallBrowserTheme": {
+        sendAsyncMessage("LightWeightThemeWebInstaller:Install", {
+          baseURI: event.target.baseURI,
+          themeData: event.target.getAttribute("data-browsertheme"),
+        });
+        break;
+      }
+      case "PreviewBrowserTheme": {
+        sendAsyncMessage("LightWeightThemeWebInstaller:Preview", {
+          baseURI: event.target.baseURI,
+          themeData: event.target.getAttribute("data-browsertheme"),
+        });
+        this._previewWindow = event.target.ownerDocument.defaultView;
+        this._previewWindow.addEventListener("pagehide", this, true);
+        break;
+      }
+      case "pagehide": {
+        sendAsyncMessage("LightWeightThemeWebInstaller:ResetPreview");
+        this._resetPreviewWindow();
+        break;
+      }
+      case "ResetBrowserThemePreview": {
+        if (this._previewWindow) {
+          sendAsyncMessage("LightWeightThemeWebInstaller:ResetPreview",
+                           {baseURI: event.target.baseURI});
+          this._resetPreviewWindow();
+        }
+        break;
+      }
+    }
+  },
+
+  _resetPreviewWindow: function () {
+    this._previewWindow.removeEventListener("pagehide", this, true);
+    this._previewWindow = null;
+  }
+};
+
+LightWeightThemeWebInstallListener.init();
+
 function disableSetDesktopBackground(aTarget) {
   // Disable the Set as Desktop Background menu item if we're still trying
   // to load the image or the load failed.
   if (!(aTarget instanceof Ci.nsIImageLoadingContent))
     return true;
 
   if (("complete" in aTarget) && !aTarget.complete)
     return true;
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -222,17 +222,16 @@ skip-if = toolkit != "cocoa"
 [browser_bug581242.js]
 [browser_bug581253.js]
 [browser_bug581947.js]
 [browser_bug585558.js]
 [browser_bug585785.js]
 [browser_bug585830.js]
 [browser_bug590206.js]
 [browser_bug592338.js]
-skip-if = e10s # Bug 653065 - Make the lightweight theme web installer ready for e10s
 [browser_bug594131.js]
 [browser_bug595507.js]
 [browser_bug596687.js]
 [browser_bug597218.js]
 [browser_bug609700.js]
 [browser_bug623155.js]
 [browser_bug623893.js]
 [browser_bug624734.js]
--- a/browser/base/content/test/general/browser_bug592338.js
+++ b/browser/base/content/test/general/browser_bug592338.js
@@ -25,18 +25,17 @@ function test_install_http() {
   gBrowser.selectedTab = gBrowser.addTab("http://example.org/browser/browser/base/content/test/general/bug592338.html");
   gBrowser.selectedBrowser.addEventListener("pageshow", function() {
     if (gBrowser.contentDocument.location.href == "about:blank")
       return;
 
     gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee, false);
 
     executeSoon(function() {
-      var link = gBrowser.contentDocument.getElementById("theme-install");
-      EventUtils.synthesizeMouse(link, 2, 2, {}, gBrowser.contentWindow);
+      BrowserTestUtils.synthesizeMouse("#theme-install", 2, 2, {}, gBrowser.selectedBrowser);
 
       is(LightweightThemeManager.currentTheme, null, "Should not have installed the test theme");
 
       gBrowser.removeTab(gBrowser.selectedTab);
 
       pm.remove(makeURI("http://example.org/"), "install");
 
       runNextTest();
@@ -52,29 +51,31 @@ function test_install_lwtheme() {
 
   gBrowser.selectedTab = gBrowser.addTab("https://example.com/browser/browser/base/content/test/general/bug592338.html");
   gBrowser.selectedBrowser.addEventListener("pageshow", function() {
     if (gBrowser.contentDocument.location.href == "about:blank")
       return;
 
     gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee, false);
 
-    executeSoon(function() {
-      var link = gBrowser.contentDocument.getElementById("theme-install");
-      EventUtils.synthesizeMouse(link, 2, 2, {}, gBrowser.contentWindow);
-
-      is(LightweightThemeManager.currentTheme.id, "test", "Should have installed the test theme");
+    BrowserTestUtils.synthesizeMouse("#theme-install", 2, 2, {}, gBrowser.selectedBrowser);
+    let notification;
+    let notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
+    waitForCondition(
+      () => (notification = notificationBox.getNotificationWithValue("lwtheme-install-notification")),
+      () => {
+        is(LightweightThemeManager.currentTheme.id, "test", "Should have installed the test theme");
 
-      LightweightThemeManager.currentTheme = null;
-      gBrowser.removeTab(gBrowser.selectedTab);
+        LightweightThemeManager.currentTheme = null;
+        gBrowser.removeTab(gBrowser.selectedTab);
+        Services.perms.remove(makeURI("http://example.com/"), "install");
 
-      Services.perms.remove(makeURI("http://example.com/"), "install");
-
-      runNextTest();
-    });
+        runNextTest();
+      }
+    );
   }, false);
 },
 
 function test_lwtheme_switch_theme() {
   is(LightweightThemeManager.currentTheme, null, "Should be no lightweight theme selected");
 
   AddonManager.getAddonByID("theme-xpi@tests.mozilla.org", function(aAddon) {
     aAddon.userDisabled = false;
@@ -87,17 +88,16 @@ function test_lwtheme_switch_theme() {
     gBrowser.selectedTab = gBrowser.addTab("https://example.com/browser/browser/base/content/test/general/bug592338.html");
     gBrowser.selectedBrowser.addEventListener("pageshow", function() {
       if (gBrowser.contentDocument.location.href == "about:blank")
         return;
 
       gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee, false);
 
       executeSoon(function() {
-        var link = gBrowser.contentDocument.getElementById("theme-install");
         wait_for_notification(function(aPanel) {
           is(LightweightThemeManager.currentTheme, null, "Should not have installed the test lwtheme");
           ok(aAddon.isActive, "Test theme should still be active");
 
           let notification = aPanel.childNodes[0];
           is(notification.button.label, "Restart Now", "Should have seen the right button");
 
           ok(aAddon.userDisabled, "Should be waiting to disable the test theme");
@@ -105,17 +105,17 @@ function test_lwtheme_switch_theme() {
           Services.prefs.setBoolPref("extensions.dss.enabled", true);
 
           gBrowser.removeTab(gBrowser.selectedTab);
 
           Services.perms.remove(makeURI("http://example.com"), "install");
 
           runNextTest();
         });
-        EventUtils.synthesizeMouse(link, 2, 2, {}, gBrowser.contentWindow);
+        BrowserTestUtils.synthesizeMouse("#theme-install", 2, 2, {}, gBrowser.selectedBrowser);
       });
     }, false);
   });
 }
 ];
 
 function runNextTest() {
   AddonManager.getAllInstalls(function(aInstalls) {
--- a/browser/base/content/test/general/browser_trackingUI_1.js
+++ b/browser/base/content/test/general/browser_trackingUI_1.js
@@ -40,49 +40,56 @@ function clickButton(sel) {
   let win = browser.ownerGlobal;
   let el = win.document.querySelector(sel);
   el.doCommand();
 }
 
 function testBenignPage() {
   info("Non-tracking content must not be blocked");
   ok (!TrackingProtection.container.hidden, "The container is visible");
-  ok (!TrackingProtection.content.hasAttribute("block-disabled"), "blocking not disabled");
-  ok (!TrackingProtection.content.hasAttribute("block-active"), "blocking is not active");
+  ok (!TrackingProtection.content.hasAttribute("state"), "content: no state");
+  ok (!TrackingProtection.icon.hasAttribute("state"), "icon: no state");
 
+  ok (hidden("#tracking-protection-icon"), "icon is hidden");
   ok (hidden("#tracking-action-block"), "blockButton is hidden");
   ok (hidden("#tracking-action-unblock"), "unblockButton is hidden");
 
   // Make sure that the no tracking elements message appears
   ok (!hidden("#tracking-not-detected"), "labelNoTracking is visible");
   ok (hidden("#tracking-loaded"), "labelTrackingLoaded is hidden");
   ok (hidden("#tracking-blocked"), "labelTrackingBlocked is hidden");
 }
 
 function testTrackingPage() {
   info("Tracking content must be blocked");
   ok (!TrackingProtection.container.hidden, "The container is visible");
-  ok (!TrackingProtection.content.hasAttribute("block-disabled"), "blocking not disabled");
-  ok (TrackingProtection.content.hasAttribute("block-active"), "blocking is active");
+  is (TrackingProtection.content.getAttribute("state"), "blocked-tracking-content",
+      'content: state="blocked-tracking-content"');
+  is (TrackingProtection.icon.getAttribute("state"), "blocked-tracking-content",
+      'icon: state="blocked-tracking-content"');
 
+  ok (!hidden("#tracking-protection-icon"), "icon is visible");
   ok (hidden("#tracking-action-block"), "blockButton is hidden");
   ok (!hidden("#tracking-action-unblock"), "unblockButton is visible");
 
   // Make sure that the blocked tracking elements message appears
   ok (hidden("#tracking-not-detected"), "labelNoTracking is hidden");
   ok (hidden("#tracking-loaded"), "labelTrackingLoaded is hidden");
   ok (!hidden("#tracking-blocked"), "labelTrackingBlocked is visible");
 }
 
-function testTrackingPageWhitelisted() {
+function testTrackingPageUnblocked() {
   info("Tracking content must be white-listed and not blocked");
   ok (!TrackingProtection.container.hidden, "The container is visible");
-  ok (TrackingProtection.content.hasAttribute("block-disabled"), "blocking is disabled");
-  ok (!TrackingProtection.content.hasAttribute("block-active"), "blocking is not active");
+  is (TrackingProtection.content.getAttribute("state"), "loaded-tracking-content",
+      'content: state="loaded-tracking-content"');
+  is (TrackingProtection.icon.getAttribute("state"), "loaded-tracking-content",
+      'icon: state="loaded-tracking-content"');
 
+  ok (!hidden("#tracking-protection-icon"), "icon is visible");
   ok (!hidden("#tracking-action-block"), "blockButton is visible");
   ok (hidden("#tracking-action-unblock"), "unblockButton is hidden");
 
   // Make sure that the blocked tracking elements message appears
   ok (hidden("#tracking-not-detected"), "labelNoTracking is hidden");
   ok (!hidden("#tracking-loaded"), "labelTrackingLoaded is visible");
   ok (hidden("#tracking-blocked"), "labelTrackingBlocked is hidden");
 }
@@ -92,27 +99,25 @@ function* testTrackingProtectionForTab(t
   yield promiseTabLoadEvent(tab, BENIGN_PAGE);
   testBenignPage();
 
   info("Load a test page containing tracking elements");
   yield promiseTabLoadEvent(tab, TRACKING_PAGE);
   testTrackingPage();
 
   info("Disable TP for the page (which reloads the page)");
+  let tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-unblock");
-
-  info("Wait for tab to reload following TP white-listing");
-  yield promiseTabLoadEvent(tab);
-  testTrackingPageWhitelisted();
+  yield tabReloadPromise;
+  testTrackingPageUnblocked();
 
   info("Re-enable TP for the page (which reloads the page)");
+  tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-block");
-
-  info("Wait for tab to reload following TP black-listing");
-  yield promiseTabLoadEvent(tab);
+  yield tabReloadPromise;
   testTrackingPage();
 }
 
 add_task(function* testNormalBrowsing() {
   yield UrlClassifierTestUtils.addTestTrackers();
 
   browser = gBrowser;
   let tab = browser.selectedTab = browser.addTab();
--- a/browser/base/content/test/plugins/browser.ini
+++ b/browser/base/content/test/plugins/browser.ini
@@ -50,16 +50,17 @@ support-files =
 [browser_clearplugindata.js]
 [browser_CTP_context_menu.js]
 skip-if = toolkit == "gtk2" || toolkit == "gtk3"   # fails intermittently on Linux (bug 909342)
 [browser_CTP_crashreporting.js]
 skip-if = !crashreporter
 [browser_CTP_data_urls.js]
 [browser_CTP_drag_drop.js]
 [browser_CTP_hide_overlay.js]
+skip-if = true # Bug 1160788
 [browser_CTP_iframe.js]
 skip-if = os == 'linux' || os == 'mac' # Bug 984821
 [browser_CTP_multi_allow.js]
 [browser_CTP_nonplugins.js]
 [browser_CTP_notificationBar.js]
 [browser_CTP_outsideScrollArea.js]
 [browser_CTP_remove_navigate.js]
 [browser_CTP_resize.js]
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -1461,17 +1461,17 @@ html[dir="rtl"] .standalone .room-conver
 .text-chat-entries {
   overflow: auto;
 }
 
 .text-chat-entry {
   display: flex;
   flex-direction: row;
   margin-bottom: .5em;
-  text-align: end;
+  text-align: start;
   flex-wrap: nowrap;
   justify-content: flex-start;
   align-content: stretch;
   align-items: flex-start;
 }
 
 .text-chat-entry > p {
   position: relative;
old mode 100644
new mode 100755
--- a/browser/components/loop/content/shared/libs/sdk.js
+++ b/browser/components/loop/content/shared/libs/sdk.js
@@ -1,31 +1,31 @@
 /**
- * @license  OpenTok JavaScript Library v2.5.1 23265fa HEAD
+ * @license  OpenTok JavaScript Library v2.5.2 f4508e1 2015Q1.patch.1
  * http://www.tokbox.com/
  *
  * Copyright (c) 2014 TokBox, Inc.
  * Released under the MIT license
  * http://opensource.org/licenses/MIT
  *
- * Date: April 13 06:37:42 2015
+ * Date: July 13 05:38:08 2015
  */
 
 
 !(function(window) {
 
 !(function(window, OTHelpers, undefined) {
 
 /**
- * @license  Common JS Helpers on OpenTok 0.3.0 058dfa5 2015Q1
+ * @license  Common JS Helpers on OpenTok 0.3.0 f151b47 HEAD
  * http://www.tokbox.com/
  *
  * Copyright (c) 2015 TokBox, Inc.
  *
- * Date: April 13 06:37:28 2015
+ * Date: July 13 05:37:51 2015
  *
  */
 
 
 // OT Helper Methods
 //
 // helpers.js                           <- the root file
 // helpers/lib/{helper topic}.js        <- specialised helpers for specific tasks/topics
@@ -3711,16 +3711,29 @@ OTHelpers.getCookie = function(key) {
     if (value) {
       return value;
     }
   }
 
   return null;
 };
 
+/*jshint browser:true, smarttabs:true*/
+
+// tb_require('../helpers.js')
+
+OTHelpers.castToBoolean = function(value, defaultValue) {
+  if (value === undefined) return defaultValue;
+  return value === 'true' || value === true;
+};
+
+OTHelpers.roundFloat = function(value, places) {
+  return Number(value.toFixed(places));
+};
+
 // tb_require('../helpers.js')
 
 /* jshint globalstrict: true, strict: false, undef: true, unused: true,
           trailing: true, browser: true, smarttabs:true */
 
 
 OTHelpers.Collection = function(idField) {
   var _models = [],
@@ -3894,29 +3907,16 @@ OTHelpers.Collection = function(idField)
   };
 };
 
 
 /*jshint browser:true, smarttabs:true*/
 
 // tb_require('../helpers.js')
 
-OTHelpers.castToBoolean = function(value, defaultValue) {
-  if (value === undefined) return defaultValue;
-  return value === 'true' || value === true;
-};
-
-OTHelpers.roundFloat = function(value, places) {
-  return Number(value.toFixed(places));
-};
-
-/*jshint browser:true, smarttabs:true*/
-
-// tb_require('../helpers.js')
-
 (function() {
 
   var capabilities = {};
 
   // Registers a new capability type and a function that will indicate
   // whether this client has that capability.
   //
   //   OTHelpers.registerCapability('bundle', function() {
@@ -6100,22 +6100,22 @@ OTHelpers.post = function(url, options, 
   }
 };
 
 
 })(window, window.OTHelpers);
 
 
 /**
- * @license  TB Plugin 0.4.0.10 01e58ad 2015Q1
+ * @license  TB Plugin 0.4.0.10 6935b20 HEAD
  * http://www.tokbox.com/
  *
  * Copyright (c) 2015 TokBox, Inc.
  *
- * Date: April 13 06:37:38 2015
+ * Date: July 13 05:38:06 2015
  *
  */
 
 /* global scope:true */
 /* exported OTPlugin */
 
 /* jshint ignore:start */
 (function(scope) {
@@ -7421,106 +7421,16 @@ var MediaConstraints = function(userCons
 
   this.toHash = function() {
     return constraints;
   };
 };
 
 // tb_require('./header.js')
 // tb_require('./shims.js')
-
-/* global scope:true */
-/* exported  createFrame */
-
-var createFrame = function createFrame (bodyContent, callbackId, callback) {
-  var Proto = function Frame () {},
-      api = new Proto(),
-      domElement = scope.document.createElement('iframe');
-
-  domElement.id = 'OTPlugin_frame_' + $.uuid().replace(/\-+/g, '');
-  domElement.style.border = '0';
-
-  try {
-    domElement.style.backgroundColor = 'rgba(0,0,0,0)';
-  } catch (err) {
-    // Old IE browsers don't support rgba
-    domElement.style.backgroundColor = 'transparent';
-    domElement.setAttribute('allowTransparency', 'true');
-  }
-
-  domElement.scrolling = 'no';
-  domElement.setAttribute('scrolling', 'no');
-
-  // This is necessary for IE, as it will not inherit it's doctype from
-  // the parent frame.
-  var frameContent = '<!DOCTYPE html><html><head>' +
-                    '<meta http-equiv="x-ua-compatible" content="IE=Edge">' +
-                    '<meta http-equiv="Content-type" content="text/html; charset=utf-8">' +
-                    '<title></title></head><body>' +
-                    bodyContent +
-                    '<script>window.parent["' + callbackId + '"](' +
-                      'document.querySelector("object")' +
-                    ');</script></body></html>';
-
-  var wrappedCallback = function() {
-    OTPlugin.log('LOADED IFRAME');
-    var doc = domElement.contentDocument || domElement.contentWindow.document;
-
-    if ($.env.iframeNeedsLoad) {
-      doc.body.style.backgroundColor = 'transparent';
-      doc.body.style.border = 'none';
-
-      if ($.env.name !== 'IE') {
-        // Skip this for IE as we use the bookmarklet workaround
-        // for THAT browser.
-        doc.open();
-        doc.write(frameContent);
-        doc.close();
-      }
-    }
-
-    if (callback) {
-      callback(
-        api,
-        domElement.contentWindow,
-        doc
-      );
-    }
-  };
-
-  scope.document.body.appendChild(domElement);
-
-  if($.env.iframeNeedsLoad) {
-    if ($.env.name === 'IE') {
-      // This works around some issues with IE and document.write.
-      // Basically this works by slightly abusing the bookmarklet/scriptlet
-      // functionality that all browsers support.
-      domElement.contentWindow.contents = frameContent;
-      /*jshint scripturl:true*/
-      domElement.src = 'javascript:window["contents"]';
-      /*jshint scripturl:false*/
-    }
-
-    $.on(domElement, 'load', wrappedCallback);
-  } else {
-    setTimeout(wrappedCallback, 0);
-  }
-
-  api.reparent = function reparent (target) {
-    // document.adoptNode(domElement);
-    target.appendChild(domElement);
-  };
-
-  api.element = domElement;
-
-  return api;
-};
-
-// tb_require('./header.js')
-// tb_require('./shims.js')
 // tb_require('./plugin_proxies.js')
 
 /* global OT:true, OTPlugin:true, ActiveXObject:true,
           PluginProxies:true, curryCallAsync:true */
 
 /* exported AutoUpdater:true */
 var AutoUpdater;
 
@@ -7842,16 +7752,106 @@ var // jshint -W098
 
           $.onDOMUnload(destroy);
         });
       });
     };
 
 // tb_require('./header.js')
 // tb_require('./shims.js')
+
+/* global scope:true */
+/* exported  createFrame */
+
+var createFrame = function createFrame (bodyContent, callbackId, callback) {
+  var Proto = function Frame () {},
+      api = new Proto(),
+      domElement = scope.document.createElement('iframe');
+
+  domElement.id = 'OTPlugin_frame_' + $.uuid().replace(/\-+/g, '');
+  domElement.style.border = '0';
+
+  try {
+    domElement.style.backgroundColor = 'rgba(0,0,0,0)';
+  } catch (err) {
+    // Old IE browsers don't support rgba
+    domElement.style.backgroundColor = 'transparent';
+    domElement.setAttribute('allowTransparency', 'true');
+  }
+
+  domElement.scrolling = 'no';
+  domElement.setAttribute('scrolling', 'no');
+
+  // This is necessary for IE, as it will not inherit it's doctype from
+  // the parent frame.
+  var frameContent = '<!DOCTYPE html><html><head>' +
+                    '<meta http-equiv="x-ua-compatible" content="IE=Edge">' +
+                    '<meta http-equiv="Content-type" content="text/html; charset=utf-8">' +
+                    '<title></title></head><body>' +
+                    bodyContent +
+                    '<script>window.parent["' + callbackId + '"](' +
+                      'document.querySelector("object")' +
+                    ');</script></body></html>';
+
+  var wrappedCallback = function() {
+    OTPlugin.log('LOADED IFRAME');
+    var doc = domElement.contentDocument || domElement.contentWindow.document;
+
+    if ($.env.iframeNeedsLoad) {
+      doc.body.style.backgroundColor = 'transparent';
+      doc.body.style.border = 'none';
+
+      if ($.env.name !== 'IE') {
+        // Skip this for IE as we use the bookmarklet workaround
+        // for THAT browser.
+        doc.open();
+        doc.write(frameContent);
+        doc.close();
+      }
+    }
+
+    if (callback) {
+      callback(
+        api,
+        domElement.contentWindow,
+        doc
+      );
+    }
+  };
+
+  scope.document.body.appendChild(domElement);
+
+  if($.env.iframeNeedsLoad) {
+    if ($.env.name === 'IE') {
+      // This works around some issues with IE and document.write.
+      // Basically this works by slightly abusing the bookmarklet/scriptlet
+      // functionality that all browsers support.
+      domElement.contentWindow.contents = frameContent;
+      /*jshint scripturl:true*/
+      domElement.src = 'javascript:window["contents"]';
+      /*jshint scripturl:false*/
+    }
+
+    $.on(domElement, 'load', wrappedCallback);
+  } else {
+    setTimeout(wrappedCallback, 0);
+  }
+
+  api.reparent = function reparent (target) {
+    // document.adoptNode(domElement);
+    target.appendChild(domElement);
+  };
+
+  api.element = domElement;
+
+  return api;
+};
+
+// tb_require('./header.js')
+// tb_require('./shims.js')
 // tb_require('./proxy.js')
 // tb_require('./auto_updater.js')
 // tb_require('./media_constraints.js')
 // tb_require('./peer_connection.js')
 // tb_require('./media_stream.js')
 // tb_require('./video_container.js')
 // tb_require('./rumor.js')
 // tb_require('./lifecycle.js')
@@ -8060,18 +8060,18 @@ OT.APIKEY = (function(){
 
 
 if (!window.OT) window.OT = OT;
 if (!window.TB) window.TB = OT;
 
 // tb_require('../js/ot.js')
 
 OT.properties = {
-  version: 'v2.5.1',         // The current version (eg. v2.0.4) (This is replaced by gradle)
-  build: '23265fa',    // The current build hash (This is replaced by gradle)
+  version: 'v2.5.2',         // The current version (eg. v2.0.4) (This is replaced by gradle)
+  build: 'f4508e1',    // The current build hash (This is replaced by gradle)
 
   // Whether or not to turn on debug logging by default
   debug: 'false',
   // The URL of the tokbox website
   websiteURL: 'http://www.tokbox.com',
 
   // The URL of the CDN
   cdnURL: 'http://static.opentok.com',
@@ -8248,132 +8248,16 @@ OT.$.userAgent = function() { return OT.
   * @name OT.log
   * @memberof OT
   * @function
   * @see <a href="#setLogLevel">OT.setLogLevel()</a>
   */
 
 // tb_require('../../../helpers/helpers.js')
 
-/* jshint globalstrict: true, strict: false, undef: true, unused: true,
-          trailing: true, browser: true, smarttabs:true */
-/* global OT */
-
-// Rumor Messaging for JS
-//
-// https://tbwiki.tokbox.com/index.php/Rumor_:_Messaging_FrameWork
-//
-// @todo Rumor {
-//     Add error codes for all the error cases
-//     Add Dependability commands
-// }
-
-OT.Rumor = {
-  MessageType: {
-    // This is used to subscribe to address/addresses. The address/addresses the
-    // client specifies here is registered on the server. Once any message is sent to
-    // that address/addresses, the client receives that message.
-    SUBSCRIBE: 0,
-
-    // This is used to unsubscribe to address / addresses. Once the client unsubscribe
-    // to an address, it will stop getting messages sent to that address.
-    UNSUBSCRIBE: 1,
-
-    // This is used to send messages to arbitrary address/ addresses. Messages can be
-    // anything and Rumor will not care about what is included.
-    MESSAGE: 2,
-
-    // This will be the first message that the client sends to the server. It includes
-    // the uniqueId for that client connection and a disconnect_notify address that will
-    // be notified once the client disconnects.
-    CONNECT: 3,
-
-    // This will be the message used by the server to notify an address that a
-    // client disconnected.
-    DISCONNECT: 4,
-
-    //Enhancements to support Keepalives
-    PING: 7,
-    PONG: 8,
-    STATUS: 9
-  }
-};
-
-// tb_require('../../../helpers/helpers.js')
-// tb_require('./rumor.js')
-
-/* jshint globalstrict: true, strict: false, undef: true, unused: true,
-          trailing: true, browser: true, smarttabs:true */
-/* global OT, OTPlugin */
-
-!(function() {
-
-  OT.Rumor.PluginSocket = function(messagingURL, events) {
-
-    var webSocket,
-        state = 'initializing';
-
-    OTPlugin.initRumorSocket(messagingURL, OT.$.bind(function(err, rumorSocket) {
-      if(err) {
-        state = 'closed';
-        events.onClose({ code: 4999 });
-      } else if(state === 'initializing') {
-        webSocket = rumorSocket;
-
-        webSocket.onOpen(function() {
-          state = 'open';
-          events.onOpen();
-        });
-        webSocket.onClose(function(error) {
-          state = 'closed'; /* CLOSED */
-          events.onClose({ code: error });
-        });
-        webSocket.onError(function(error) {
-          state = 'closed'; /* CLOSED */
-          events.onError(error);
-          /* native websockets seem to do this, so should we */
-          events.onClose({ code: error });
-        });
-
-        webSocket.onMessage(function(type, addresses, headers, payload) {
-          var msg = new OT.Rumor.Message(type, addresses, headers, payload);
-          events.onMessage(msg);
-        });
-
-        webSocket.open();
-      } else {
-        this.close();
-      }
-    }, this));
-
-    this.close = function() {
-      if(state === 'initializing' || state === 'closed') {
-        state = 'closed';
-        return;
-      }
-
-      webSocket.close(1000, '');
-    };
-
-    this.send = function(msg) {
-      if(state === 'open') {
-        webSocket.send(msg);
-      }
-    };
-
-    this.isClosed = function() {
-      return state === 'closed';
-    };
-
-  };
-
-}(this));
-
-// tb_require('../../../helpers/helpers.js')
-
 // https://code.google.com/p/stringencoding/
 // An implementation of http://encoding.spec.whatwg.org/#api
 // Modified by TokBox to remove all encoding support except for utf-8
 
 /**
  * @license  Copyright 2014 Joshua Bell
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -9420,16 +9304,132 @@ OT.Rumor = {
   global['TextEncoder'] = global['TextEncoder'] || TextEncoder;
   global['TextDecoder'] = global['TextDecoder'] || TextDecoder;
 
   /* jshint ignore:end */
 
 }(this));
 
 // tb_require('../../../helpers/helpers.js')
+
+/* jshint globalstrict: true, strict: false, undef: true, unused: true,
+          trailing: true, browser: true, smarttabs:true */
+/* global OT */
+
+// Rumor Messaging for JS
+//
+// https://tbwiki.tokbox.com/index.php/Rumor_:_Messaging_FrameWork
+//
+// @todo Rumor {
+//     Add error codes for all the error cases
+//     Add Dependability commands
+// }
+
+OT.Rumor = {
+  MessageType: {
+    // This is used to subscribe to address/addresses. The address/addresses the
+    // client specifies here is registered on the server. Once any message is sent to
+    // that address/addresses, the client receives that message.
+    SUBSCRIBE: 0,
+
+    // This is used to unsubscribe to address / addresses. Once the client unsubscribe
+    // to an address, it will stop getting messages sent to that address.
+    UNSUBSCRIBE: 1,
+
+    // This is used to send messages to arbitrary address/ addresses. Messages can be
+    // anything and Rumor will not care about what is included.
+    MESSAGE: 2,
+
+    // This will be the first message that the client sends to the server. It includes
+    // the uniqueId for that client connection and a disconnect_notify address that will
+    // be notified once the client disconnects.
+    CONNECT: 3,
+
+    // This will be the message used by the server to notify an address that a
+    // client disconnected.
+    DISCONNECT: 4,
+
+    //Enhancements to support Keepalives
+    PING: 7,
+    PONG: 8,
+    STATUS: 9
+  }
+};
+
+// tb_require('../../../helpers/helpers.js')
+// tb_require('./rumor.js')
+
+/* jshint globalstrict: true, strict: false, undef: true, unused: true,
+          trailing: true, browser: true, smarttabs:true */
+/* global OT, OTPlugin */
+
+!(function() {
+
+  OT.Rumor.PluginSocket = function(messagingURL, events) {
+
+    var webSocket,
+        state = 'initializing';
+
+    OTPlugin.initRumorSocket(messagingURL, OT.$.bind(function(err, rumorSocket) {
+      if(err) {
+        state = 'closed';
+        events.onClose({ code: 4999 });
+      } else if(state === 'initializing') {
+        webSocket = rumorSocket;
+
+        webSocket.onOpen(function() {
+          state = 'open';
+          events.onOpen();
+        });
+        webSocket.onClose(function(error) {
+          state = 'closed'; /* CLOSED */
+          events.onClose({ code: error });
+        });
+        webSocket.onError(function(error) {
+          state = 'closed'; /* CLOSED */
+          events.onError(error);
+          /* native websockets seem to do this, so should we */
+          events.onClose({ code: error });
+        });
+
+        webSocket.onMessage(function(type, addresses, headers, payload) {
+          var msg = new OT.Rumor.Message(type, addresses, headers, payload);
+          events.onMessage(msg);
+        });
+
+        webSocket.open();
+      } else {
+        this.close();
+      }
+    }, this));
+
+    this.close = function() {
+      if(state === 'initializing' || state === 'closed') {
+        state = 'closed';
+        return;
+      }
+
+      webSocket.close(1000, '');
+    };
+
+    this.send = function(msg) {
+      if(state === 'open') {
+        webSocket.send(msg);
+      }
+    };
+
+    this.isClosed = function() {
+      return state === 'closed';
+    };
+
+  };
+
+}(this));
+
+// tb_require('../../../helpers/helpers.js')
 // tb_require('./encoding.js')
 // tb_require('./rumor.js')
 
 /* jshint globalstrict: true, strict: false, undef: true, unused: true,
           trailing: true, browser: true, smarttabs:true */
 /* global OT, TextEncoder, TextDecoder */
 
 //
@@ -16553,16 +16553,150 @@ var loadCSS = function loadCSS(cssURL) {
   style.media = 'screen';
   style.rel = 'stylesheet';
   style.href = cssURL;
   var head = document.head || document.getElementsByTagName('head')[0];
   head.appendChild(style);
 };
 
 // tb_require('../helpers.js')
+
+/* jshint globalstrict: true, strict: false, undef: true, unused: true,
+          trailing: true, browser: true, smarttabs:true */
+/* global OTPlugin, OT */
+
+///
+// Capabilities
+//
+// Support functions to query browser/client Media capabilities.
+//
+
+
+// Indicates whether this client supports the getUserMedia
+// API.
+//
+OT.$.registerCapability('getUserMedia', function() {
+  if (OT.$.env === 'Node') return false;
+  return !!(navigator.webkitGetUserMedia ||
+            navigator.mozGetUserMedia ||
+            OTPlugin.isInstalled());
+});
+
+
+
+// TODO Remove all PeerConnection stuff, that belongs to the messaging layer not the Media layer.
+// Indicates whether this client supports the PeerConnection
+// API.
+//
+// Chrome Issues:
+// * The explicit prototype.addStream check is because webkitRTCPeerConnection was
+// partially implemented, but not functional, in Chrome 22.
+//
+// Firefox Issues:
+// * No real support before Firefox 19
+// * Firefox 19 has issues with generating Offers.
+// * Firefox 20 doesn't interoperate with Chrome.
+//
+OT.$.registerCapability('PeerConnection', function() {
+  if (OT.$.env === 'Node') {
+    return false;
+  }
+  else if (typeof(window.webkitRTCPeerConnection) === 'function' &&
+                    !!window.webkitRTCPeerConnection.prototype.addStream) {
+    return true;
+  } else if (typeof(window.mozRTCPeerConnection) === 'function' && OT.$.env.version > 20.0) {
+    return true;
+  } else {
+    return OTPlugin.isInstalled();
+  }
+});
+
+
+
+// Indicates whether this client supports WebRTC
+//
+// This is defined as: getUserMedia + PeerConnection + exceeds min browser version
+//
+OT.$.registerCapability('webrtc', function() {
+  if (OT.properties) {
+    var minimumVersions = OT.properties.minimumVersion || {},
+        minimumVersion = minimumVersions[OT.$.env.name.toLowerCase()];
+
+    if(minimumVersion && OT.$.env.versionGreaterThan(minimumVersion)) {
+      OT.debug('Support for', OT.$.env.name, 'is disabled because we require',
+        minimumVersion, 'but this is', OT.$.env.version);
+      return false;
+    }
+  }
+
+  if (OT.$.env === 'Node') {
+    // Node works, even though it doesn't have getUserMedia
+    return true;
+  }
+
+  return OT.$.hasCapabilities('getUserMedia', 'PeerConnection');
+});
+
+
+// TODO Remove all transport stuff, that belongs to the messaging layer not the Media layer.
+// Indicates if the browser supports bundle
+//
+// Broadly:
+// * Firefox doesn't support bundle
+// * Chrome support bundle
+// * OT Plugin supports bundle
+// * We assume NodeJs supports bundle (e.g. 'you're on your own' mode)
+//
+OT.$.registerCapability('bundle', function() {
+  return OT.$.hasCapabilities('webrtc') &&
+            (OT.$.env.name === 'Chrome' ||
+              OT.$.env.name === 'Node' ||
+              OTPlugin.isInstalled());
+});
+
+// Indicates if the browser supports RTCP Mux
+//
+// Broadly:
+// * Older versions of Firefox (<= 25) don't support RTCP Mux
+// * Older versions of Firefox (>= 26) support RTCP Mux (not tested yet)
+// * Chrome support RTCP Mux
+// * OT Plugin supports RTCP Mux
+// * We assume NodeJs supports RTCP Mux (e.g. 'you're on your own' mode)
+//
+OT.$.registerCapability('RTCPMux', function() {
+  return OT.$.hasCapabilities('webrtc') &&
+              (OT.$.env.name === 'Chrome' ||
+                OT.$.env.name === 'Node' ||
+                OTPlugin.isInstalled());
+});
+
+
+
+// Indicates whether this browser supports the getMediaDevices (getSources) API.
+//
+OT.$.registerCapability('getMediaDevices', function() {
+  return OT.$.isFunction(window.MediaStreamTrack) &&
+            OT.$.isFunction(window.MediaStreamTrack.getSources);
+});
+
+
+OT.$.registerCapability('audioOutputLevelStat', function() {
+  return OT.$.env.name === 'Chrome' || OT.$.env.name === 'IE';
+});
+
+OT.$.registerCapability('webAudioCapableRemoteStream', function() {
+  return OT.$.env.name === 'Firefox';
+});
+
+OT.$.registerCapability('webAudio', function() {
+  return 'AudioContext' in window;
+});
+
+
+// tb_require('../helpers.js')
 // tb_require('./properties.js')
 
 /* jshint globalstrict: true, strict: false, undef: true, unused: true,
           trailing: true, browser: true, smarttabs:true */
 /* global OT */
 
 //--------------------------------------
 // JS Dynamic Config
@@ -16702,150 +16836,16 @@ OT.Config = (function() {
   };
 
   OT.$.eventing(_this);
 
   return _this;
 })();
 
 // tb_require('../helpers.js')
-
-/* jshint globalstrict: true, strict: false, undef: true, unused: true,
-          trailing: true, browser: true, smarttabs:true */
-/* global OTPlugin, OT */
-
-///
-// Capabilities
-//
-// Support functions to query browser/client Media capabilities.
-//
-
-
-// Indicates whether this client supports the getUserMedia
-// API.
-//
-OT.$.registerCapability('getUserMedia', function() {
-  if (OT.$.env === 'Node') return false;
-  return !!(navigator.webkitGetUserMedia ||
-            navigator.mozGetUserMedia ||
-            OTPlugin.isInstalled());
-});
-
-
-
-// TODO Remove all PeerConnection stuff, that belongs to the messaging layer not the Media layer.
-// Indicates whether this client supports the PeerConnection
-// API.
-//
-// Chrome Issues:
-// * The explicit prototype.addStream check is because webkitRTCPeerConnection was
-// partially implemented, but not functional, in Chrome 22.
-//
-// Firefox Issues:
-// * No real support before Firefox 19
-// * Firefox 19 has issues with generating Offers.
-// * Firefox 20 doesn't interoperate with Chrome.
-//
-OT.$.registerCapability('PeerConnection', function() {
-  if (OT.$.env === 'Node') {
-    return false;
-  }
-  else if (typeof(window.webkitRTCPeerConnection) === 'function' &&
-                    !!window.webkitRTCPeerConnection.prototype.addStream) {
-    return true;
-  } else if (typeof(window.mozRTCPeerConnection) === 'function' && OT.$.env.version > 20.0) {
-    return true;
-  } else {
-    return OTPlugin.isInstalled();
-  }
-});
-
-
-
-// Indicates whether this client supports WebRTC
-//
-// This is defined as: getUserMedia + PeerConnection + exceeds min browser version
-//
-OT.$.registerCapability('webrtc', function() {
-  if (OT.properties) {
-    var minimumVersions = OT.properties.minimumVersion || {},
-        minimumVersion = minimumVersions[OT.$.env.name.toLowerCase()];
-
-    if(minimumVersion && OT.$.env.versionGreaterThan(minimumVersion)) {
-      OT.debug('Support for', OT.$.env.name, 'is disabled because we require',
-        minimumVersion, 'but this is', OT.$.env.version);
-      return false;
-    }
-  }
-
-  if (OT.$.env === 'Node') {
-    // Node works, even though it doesn't have getUserMedia
-    return true;
-  }
-
-  return OT.$.hasCapabilities('getUserMedia', 'PeerConnection');
-});
-
-
-// TODO Remove all transport stuff, that belongs to the messaging layer not the Media layer.
-// Indicates if the browser supports bundle
-//
-// Broadly:
-// * Firefox doesn't support bundle
-// * Chrome support bundle
-// * OT Plugin supports bundle
-// * We assume NodeJs supports bundle (e.g. 'you're on your own' mode)
-//
-OT.$.registerCapability('bundle', function() {
-  return OT.$.hasCapabilities('webrtc') &&
-            (OT.$.env.name === 'Chrome' ||
-              OT.$.env.name === 'Node' ||
-              OTPlugin.isInstalled());
-});
-
-// Indicates if the browser supports RTCP Mux
-//
-// Broadly:
-// * Older versions of Firefox (<= 25) don't support RTCP Mux
-// * Older versions of Firefox (>= 26) support RTCP Mux (not tested yet)
-// * Chrome support RTCP Mux
-// * OT Plugin supports RTCP Mux
-// * We assume NodeJs supports RTCP Mux (e.g. 'you're on your own' mode)
-//
-OT.$.registerCapability('RTCPMux', function() {
-  return OT.$.hasCapabilities('webrtc') &&
-              (OT.$.env.name === 'Chrome' ||
-                OT.$.env.name === 'Node' ||
-                OTPlugin.isInstalled());
-});
-
-
-
-// Indicates whether this browser supports the getMediaDevices (getSources) API.
-//
-OT.$.registerCapability('getMediaDevices', function() {
-  return OT.$.isFunction(window.MediaStreamTrack) &&
-            OT.$.isFunction(window.MediaStreamTrack.getSources);
-});
-
-
-OT.$.registerCapability('audioOutputLevelStat', function() {
-  return OT.$.env.name === 'Chrome' || OT.$.env.name === 'IE';
-});
-
-OT.$.registerCapability('webAudioCapableRemoteStream', function() {
-  return OT.$.env.name === 'Firefox';
-});
-
-OT.$.registerCapability('webAudio', function() {
-  return 'AudioContext' in window;
-});
-
-
-// tb_require('../helpers.js')
 // tb_require('./config.js')
 
 /* jshint globalstrict: true, strict: false, undef: true, unused: true,
           trailing: true, browser: true, smarttabs:true */
 /* global OT */
 
 
 OT.Analytics = function(loggingUrl) {
@@ -25965,19 +25965,19 @@ OT.Publisher = function(options) {
             }
 
             var replacePromises = [];
 
             Object.keys(_peerConnections).forEach(function(connectionId) {
               var peerConnection = _peerConnections[connectionId];
               peerConnection.getSenders().forEach(function(sender) {
                 if (sender.track.kind === 'audio' && newStream.getAudioTracks().length) {
-                  replacePromises.push(sender.switchTracks(newStream.getAudioTracks()[0]));
+                  replacePromises.push(sender.replaceTrack(newStream.getAudioTracks()[0]));
                 } else if (sender.track.kind === 'video' && newStream.getVideoTracks().length) {
-                  replacePromises.push(sender.switchTracks(newStream.getVideoTracks()[0]));
+                  replacePromises.push(sender.replaceTrack(newStream.getVideoTracks()[0]));
                 }
               });
             });
 
             Promise.all(replacePromises).then(resolve, reject);
           },
           function(error) {
             onStreamAvailableError(error);
@@ -26244,16 +26244,48 @@ OT.Publisher = function(options) {
  * @memberof Publisher
  * @see StreamEvent
  */
 };
 
 // Helper function to generate unique publisher ids
 OT.Publisher.nextId = OT.$.uuid;
 
+// tb_require('./helpers/lib/css_loader.js')
+// tb_require('./ot/system_requirements.js')
+// tb_require('./ot/session.js')
+// tb_require('./ot/publisher.js')
+// tb_require('./ot/subscriber.js')
+// tb_require('./ot/archive.js')
+// tb_require('./ot/connection.js')
+// tb_require('./ot/stream.js')
+// We want this to be included at the end, just before footer.js
+
+/* jshint globalstrict: true, strict: false, undef: true, unused: true,
+          trailing: true, browser: true, smarttabs:true */
+/* global loadCSS, define */
+
+// Tidy up everything on unload
+OT.onUnload(function() {
+  OT.publishers.destroy();
+  OT.subscribers.destroy();
+  OT.sessions.destroy('unloaded');
+});
+
+loadCSS(OT.properties.cssURL);
+
+// Register as a named AMD module, since TokBox could be concatenated with other
+// files that may use define, but not via a proper concatenation script that
+// understands anonymous AMD modules. A named AMD is safest and most robust
+// way to register. Uppercase TB is used because AMD module names are
+// derived from file names, and OpenTok is normally delivered in an uppercase
+// file name.
+if (typeof define === 'function' && define.amd) {
+  define( 'TB', [], function () { return TB; } );
+}
 // tb_require('../../conf/properties.js')
 // tb_require('../ot.js')
 // tb_require('./session.js')
 // tb_require('./publisher.js')
 
 /* jshint globalstrict: true, strict: false, undef: true, unused: true,
           trailing: true, browser: true, smarttabs:true */
 /* global OT */
@@ -26943,47 +26975,15 @@ OT.components = {};
  * @name exception
  * @event
  * @borrows ExceptionEvent#message as this.message
  * @memberof OT
  * @see ExceptionEvent
  */
 
 
-// tb_require('./helpers/lib/css_loader.js')
-// tb_require('./ot/system_requirements.js')
-// tb_require('./ot/session.js')
-// tb_require('./ot/publisher.js')
-// tb_require('./ot/subscriber.js')
-// tb_require('./ot/archive.js')
-// tb_require('./ot/connection.js')
-// tb_require('./ot/stream.js')
-// We want this to be included at the end, just before footer.js
-
-/* jshint globalstrict: true, strict: false, undef: true, unused: true,
-          trailing: true, browser: true, smarttabs:true */
-/* global loadCSS, define */
-
-// Tidy up everything on unload
-OT.onUnload(function() {
-  OT.publishers.destroy();
-  OT.subscribers.destroy();
-  OT.sessions.destroy('unloaded');
-});
-
-loadCSS(OT.properties.cssURL);
-
-// Register as a named AMD module, since TokBox could be concatenated with other
-// files that may use define, but not via a proper concatenation script that
-// understands anonymous AMD modules. A named AMD is safest and most robust
-// way to register. Uppercase TB is used because AMD module names are
-// derived from file names, and OpenTok is normally delivered in an uppercase
-// file name.
-if (typeof define === 'function' && define.amd) {
-  define( 'TB', [], function () { return TB; } );
-}
 // tb_require('./postscript.js')
 
 /* jshint ignore:start */
 })(window, window.OT);
 /* jshint ignore:end */
 
 })(window || exports);
\ No newline at end of file
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -161,19 +161,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource:///modules/ReaderParent.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AddonWatcher",
                                   "resource://gre/modules/AddonWatcher.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
                                   "resource://gre/modules/LightweightThemeManager.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
-                                  "resource://gre/modules/AppConstants.jsm");
-
 const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
 const PREF_PLUGINS_UPDATEURL  = "plugins.update.url";
 
 // Seconds of idle before trying to create a bookmarks backup.
 const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60;
 // Minimum interval between backups.  We try to not create more than one backup
 // per interval.
 const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
@@ -1184,27 +1181,16 @@ BrowserGlue.prototype = {
       }
       catch (ex) { /* Don't break the default prompt if telemetry is broken. */ }
 
       if (isDefault) {
         let now = Date.now().toString().slice(0, -3);
         Services.prefs.setCharPref("browser.shell.mostRecentDateSetAsDefault", now);
       }
 
-      if (Services.prefs.getIntPref("browser.shell.windows10DefaultBrowserABTest") == -1) {
-        let abTest = Math.round(Math.random());
-        Services.prefs.setIntPref("browser.shell.windows10DefaultBrowserABTest", abTest);
-      }
-
-      if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
-        let abTest = Services.prefs.getIntPref("browser.shell.windows10DefaultBrowserABTest");
-        let result = abTest * 2 + Number(isDefault);
-        Services.telemetry.getHistogramById("WIN_10_DEFAULT_BROWSER_AB_TEST").add(result);
-      }
-
       if (shouldCheck && !isDefault && !willRecoverSession) {
         Services.tm.mainThread.dispatch(function() {
           DefaultBrowserCheck.prompt(RecentWindow.getMostRecentBrowserWindow());
         }.bind(this), Ci.nsIThread.DISPATCH_NORMAL);
       }
     }
 
 #ifdef E10S_TESTING_ONLY
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -133,16 +133,23 @@ let gSyncPane = {
     let textbox = document.getElementById("fxaSyncComputerName");
     textbox.className = editMode ? "" : "plain";
     textbox.disabled = !editMode;
     document.getElementById("fxaChangeDeviceName").hidden = editMode;
     document.getElementById("fxaCancelChangeDeviceName").hidden = !editMode;
     document.getElementById("fxaSaveChangeDeviceName").hidden = !editMode;
   },
 
+  _focusComputerNameTextbox: function() {
+    let textbox = document.getElementById("fxaSyncComputerName");
+    let valLength = textbox.value.length;
+    textbox.focus();
+    textbox.setSelectionRange(valLength, valLength);
+  },
+
   _updateComputerNameValue: function(save) {
     let textbox = document.getElementById("fxaSyncComputerName");
     if (save) {
       Weave.Service.clientsEngine.localName = textbox.value;
     }
     else {
       textbox.value = Weave.Service.clientsEngine.localName;
     }
@@ -177,16 +184,17 @@ let gSyncPane = {
       if (this.selectedCount)
         this.clearSelection();
     });
     setEventListener("syncComputerName", "change", function (e) {
       gSyncUtils.changeName(e.target);
     });
     setEventListener("fxaChangeDeviceName", "click", function () {
       this._toggleComputerNameControls(true);
+      this._focusComputerNameTextbox();
     });
     setEventListener("fxaCancelChangeDeviceName", "click", function () {
       this._toggleComputerNameControls(false);
       this._updateComputerNameValue(false);
     });
     setEventListener("fxaSaveChangeDeviceName", "click", function () {
       this._toggleComputerNameControls(false);
       this._updateComputerNameValue(true);
@@ -248,16 +256,23 @@ let gSyncPane = {
     });
     setEventListener("sync-migrate-forget", "click", function () {
       fxaMigrator.forgetFxAccount();
     });
     setEventListener("sync-migrate-resend", "click", function () {
       let win = Services.wm.getMostRecentWindow("navigator:browser");
       fxaMigrator.resendVerificationMail(win);
     });
+    setEventListener("fxaSyncComputerName", "keypress", function (e) {
+      if (e.keyCode == KeyEvent.DOM_VK_RETURN) {
+        document.getElementById("fxaSaveChangeDeviceName").click();
+      } else if (e.keyCode == KeyEvent.DOM_VK_ESCAPE) {
+        document.getElementById("fxaCancelChangeDeviceName").click();
+      }
+    });
   },
 
   _initProfileImageUI: function () {
     try {
       if (Services.prefs.getBoolPref("identity.fxaccounts.profile_image.enabled")) {
         document.getElementById("fxaProfileImage").hidden = false;
       }
     } catch (e) { }
--- a/browser/components/preferences/in-content/sync.xul
+++ b/browser/components/preferences/in-content/sync.xul
@@ -313,43 +313,45 @@
                     preference="engine.addons"/>
           <checkbox label="&engine.prefs.label;"
                     accesskey="&engine.prefs.accesskey;"
                     preference="engine.prefs"/>
         </vbox>
         <spacer/>
       </hbox>
     </groupbox>
-    <vbox>
-      <caption>
-        <label accesskey="&syncDeviceName.accesskey;"
-               control="fxaSyncComputerName">
-          &fxaSyncDeviceName.label;
-        </label>
-      </caption>
-      <hbox id="fxaDeviceName">
-        <hbox flex="1">
-          <textbox id="fxaSyncComputerName" class="plain"
-                   disabled="true" flex="1"/>
+    <groupbox>
+      <vbox>
+        <caption>
+          <label accesskey="&syncDeviceName.accesskey;"
+                 control="fxaSyncComputerName">
+            &fxaSyncDeviceName.label;
+          </label>
+        </caption>
+        <hbox id="fxaDeviceName">
+          <hbox flex="1">
+            <textbox id="fxaSyncComputerName" class="plain"
+                     disabled="true" flex="1"/>
+          </hbox>
+          <hbox>
+            <button id="fxaChangeDeviceName"
+                    label="&changeSyncDeviceName.label;"/>
+            <button id="fxaCancelChangeDeviceName"
+                    label="&cancelChangeSyncDeviceName.label;"
+                    hidden="true"/>
+            <button id="fxaSaveChangeDeviceName"
+                    label="&saveChangeSyncDeviceName.label;"
+                    hidden="true"/>
+          </hbox>
         </hbox>
-        <hbox>
-          <button id="fxaChangeDeviceName"
-                  label="&changeSyncDeviceName.label;"/>
-          <button id="fxaCancelChangeDeviceName"
-                  label="&cancelChangeSyncDeviceName.label;"
-                  hidden="true"/>
-          <button id="fxaSaveChangeDeviceName"
-                  label="&saveChangeSyncDeviceName.label;"
-                  hidden="true"/>
-        </hbox>
-      </hbox>
-    </vbox>
+      </vbox>
+    </groupbox>
     <spacer flex="1"/>
-    <vbox id="tosPP-small">
+    <hbox id="tosPP-small">
       <label id="tosPP-small-ToS" class="text-link">
         &prefs.tosLink.label;
       </label>
       <label id="tosPP-small-PP" class="text-link">
         &fxaPrivacyNotice.link.label;
       </label>
-    </vbox>
+    </hbox>
   </vbox>
 </deck>
--- a/browser/components/sessionstore/test/browser_615394-SSWindowState_events.js
+++ b/browser/components/sessionstore/test/browser_615394-SSWindowState_events.js
@@ -37,17 +37,17 @@ function getOuterWindowID(aWindow) {
   return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
          getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
 }
 
 function test() {
   /** Test for Bug 615394 - Session Restore should notify when it is beginning and ending a restore **/
   waitForExplicitFinish();
   // Preemptively extend the timeout to prevent [orange]
-  requestLongerTimeout(2);
+  requestLongerTimeout(4);
   runNextTest();
 }
 
 
 let tests = [
   test_setTabState,
   test_duplicateTab,
   test_undoCloseTab,
--- a/browser/components/shell/nsWindowsShellService.cpp
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -725,17 +725,16 @@ nsWindowsShellService::SetDefaultBrowser
 
   if (aForAllUsers) {
     appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal");
   } else {
     appHelperPath.AppendLiteral(" /SetAsDefaultAppUser");
   }
 
   nsresult rv = LaunchHelper(appHelperPath);
-  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (NS_SUCCEEDED(rv) && IsWin8OrLater()) {
     if (aClaimAllTypes) {
       if (IsWin10OrLater()) {
         rv = LaunchModernSettingsDialogDefaultApps();
       } else {
         rv = LaunchControlPanelDefaultsSelectionUI();
       }
       // The above call should never really fail, but just in case
@@ -746,41 +745,30 @@ nsWindowsShellService::SetDefaultBrowser
         } else {
           rv = LaunchHTTPHandlerPane();
         }
       }
     } else {
       // Windows 10 blocks attempts to load the
       // HTTP Handler association dialog.
       if (IsWin10OrLater()) {
-        if (prefs) {
-          int32_t abTest;
-          rv = prefs->GetIntPref("browser.shell.windows10DefaultBrowserABTest", &abTest);
-          if (NS_SUCCEEDED(rv) && abTest == 0) {
-            rv = InvokeHTTPOpenAsVerb();
-          } else {
-            rv = LaunchModernSettingsDialogDefaultApps();
-          }
-        }
+        rv = LaunchModernSettingsDialogDefaultApps();
       } else {
         rv = LaunchHTTPHandlerPane();
       }
 
       // The above call should never really fail, but just in case
       // fall back to showing control panel for all defaults
       if (NS_FAILED(rv)) {
-        if (IsWin10OrLater()) {
-          rv = LaunchModernSettingsDialogDefaultApps();
-        } else {
-          rv = LaunchControlPanelDefaultsSelectionUI();
-        }
+        rv = LaunchControlPanelDefaultsSelectionUI();
       }
     }
   }
 
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (prefs) {
     (void) prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
--- a/browser/config/mozconfigs/linux32/l10n-mozconfig
+++ b/browser/config/mozconfigs/linux32/l10n-mozconfig
@@ -1,9 +1,8 @@
-no_tooltool=1
 no_sccache=1
 
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --with-branding=browser/branding/nightly
 
 . $topsrcdir/build/unix/mozconfig.linux32
--- a/browser/config/mozconfigs/linux64/l10n-mozconfig
+++ b/browser/config/mozconfigs/linux64/l10n-mozconfig
@@ -1,9 +1,8 @@
-no_tooltool=1
 no_sccache=1
 
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --with-branding=browser/branding/nightly
 
 . $topsrcdir/build/unix/mozconfig.linux
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/linux64/opt-tsan
@@ -0,0 +1,8 @@
+ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
+
+. $topsrcdir/build/unix/mozconfig.tsan
+
+# Need this to prevent name conflicts with the normal nightly build packages
+export MOZ_PKG_SPECIAL=tsan
+
+. "$topsrcdir/build/mozconfig.common.override"
new file mode 100644
--- /dev/null
+++ b/browser/config/tooltool-manifests/linux64/tsan.manifest
@@ -0,0 +1,12 @@
+[
+{
+"clang_version": "r241773"
+}, 
+{
+"size": 89690541, 
+"digest": "470d258d9785a120fcba65eee90daa632a42affa0f97f57d70fc8285bd76bcc27d4d0d70b6c37577ab271a04c843b6269425391a8d6df1967718dba26dd3a73d", 
+"algorithm": "sha512", 
+"filename": "clang.tar.bz2",
+"unpack": true
+}
+]
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -180,17 +180,16 @@ let DebuggerController = {
 
     yield DebuggerView.destroy();
     this.SourceScripts.disconnect();
     this.StackFrames.disconnect();
     this.ThreadState.disconnect();
     if (this._target.isTabActor) {
       this.Workers.disconnect();
     }
-    this.Tracer.disconnect();
     this.disconnect();
 
     this._shutdown = true;
   }),
 
   /**
    * Initiates remote debugging based on the current target, wiring event
    * handlers as necessary.
@@ -199,34 +198,30 @@ let DebuggerController = {
    *         A promise that is resolved when the debugger finishes connecting.
    */
   connect: Task.async(function*() {
     if (this._connected) {
       return;
     }
 
     let target = this._target;
-    let { client, form: { chromeDebugger, traceActor, actor } } = target;
+    let { client, form: { chromeDebugger, actor } } = target;
     target.on("close", this._onTabDetached);
     target.on("navigate", this._onTabNavigated);
     target.on("will-navigate", this._onTabNavigated);
     this.client = client;
 
     if (target.isAddon) {
       yield this._startAddonDebugging(actor);
     } else if (!target.isTabActor) {
       // Some actors like AddonActor or RootActor for chrome debugging
       // do not support attach/detach and can be used directly
       yield this._startChromeDebugging(chromeDebugger);
     } else {
       yield this._startDebuggingTab();
-
-      if (Prefs.tracerEnabled && traceActor) {
-        yield this._startTracingTab(traceActor);
-      }
     }
 
     this._hideUnsupportedFeatures();
   }),
 
   /**
    * Disconnects the debugger client and removes event handlers as necessary.
    */
@@ -386,41 +381,16 @@ let DebuggerController = {
 
       deferred.resolve();
     }, threadOptions);
 
     return deferred.promise;
   },
 
   /**
-   * Sets up an execution tracing session.
-   *
-   * @param object aTraceActor
-   *        The remote protocol grip of the trace actor.
-   * @return object
-   *         A promise resolved once the client attaches to the tracer.
-   */
-  _startTracingTab: function(aTraceActor) {
-    let deferred = promise.defer();
-
-    this.client.attachTracer(aTraceActor, (response, traceClient) => {
-      if (!traceClient) {
-        deferred.reject(new Error("Failed to attach to tracing actor."));
-        return;
-      }
-      this.traceClient = traceClient;
-      this.Tracer.connect();
-
-      deferred.resolve();
-    });
-
-    return deferred.promise;
-  },
-
-  /**
    * Detach and reattach to the thread actor with useSourceMaps true, blow
    * away old sources and get them again.
    */
   reconfigureThread: function({ useSourceMaps, autoBlackBox }) {
     this.activeThread.reconfigure({
       useSourceMaps: useSourceMaps,
       autoBlackBox: autoBlackBox
     }, aResponse => {
@@ -1296,17 +1266,16 @@ SourceScripts.prototype = {
         }
       });
     }
 
     // If there are any stored breakpoints for this source, display them again,
     // both in the editor and the breakpoints pane.
     DebuggerController.Breakpoints.updatePaneBreakpoints();
     DebuggerController.Breakpoints.updateEditorBreakpoints();
-    DebuggerController.HitCounts.updateEditorHitCounts();
 
     // Make sure the events listeners are up to date.
     if (DebuggerView.instrumentsPaneTab == "events-tab") {
       DebuggerController.Breakpoints.DOM.scheduleEventListenersFetch();
     }
 
     // Signal that a new source has been added.
     window.emit(EVENTS.NEW_SOURCE);
@@ -1349,17 +1318,16 @@ SourceScripts.prototype = {
     else if (!DebuggerView.Sources.selectedValue) {
       DebuggerView.Sources.selectedIndex = 0;
     }
 
     // If there are any stored breakpoints for the sources, display them again,
     // both in the editor and the breakpoints pane.
     DebuggerController.Breakpoints.updatePaneBreakpoints();
     DebuggerController.Breakpoints.updateEditorBreakpoints();
-    DebuggerController.HitCounts.updateEditorHitCounts();
 
     // Signal that sources have been added.
     window.emit(EVENTS.SOURCES_ADDED);
   },
 
   /**
    * Handler for the debugger client's 'blackboxchange' notification.
    */
@@ -1562,247 +1530,16 @@ SourceScripts.prototype = {
       }
     }
 
     return deferred.promise;
   }
 };
 
 /**
- * Tracer update the UI according to the messages exchanged with the tracer
- * actor.
- */
-function Tracer() {
-  this._trace = null;
-  this._idCounter = 0;
-  this.onTraces = this.onTraces.bind(this);
-}
-
-Tracer.prototype = {
-  get client() {
-    return DebuggerController.client;
-  },
-
-  get traceClient() {
-    return DebuggerController.traceClient;
-  },
-
-  get tracing() {
-    return !!this._trace;
-  },
-
-  /**
-   * Hooks up the debugger controller with the tracer client.
-   */
-  connect: function() {
-    this._stack = [];
-    this.client.addListener("traces", this.onTraces);
-  },
-
-  /**
-   * Disconnects the debugger controller from the tracer client. Any further
-   * communcation with the tracer actor will not have any effect on the UI.
-   */
-  disconnect: function() {
-    this._stack = null;
-    this.client.removeListener("traces", this.onTraces);
-  },
-
-  /**
-   * Instructs the tracer actor to start tracing.
-   */
-  startTracing: function(aCallback = () => {}) {
-    if (this.tracing) {
-      return;
-    }
-
-    DebuggerView.Tracer.selectTab();
-
-    let id = this._trace = "dbg.trace" + Math.random();
-    let fields = [
-      "name",
-      "location",
-      "hitCount",
-      "parameterNames",
-      "depth",
-      "arguments",
-      "return",
-      "throw",
-      "yield"
-    ];
-
-    this.traceClient.startTrace(fields, id, aResponse => {
-      const { error } = aResponse;
-      if (error) {
-        DevToolsUtils.reportException("Tracer.prototype.startTracing", error);
-        this._trace = null;
-      }
-
-      aCallback(aResponse);
-    });
-  },
-
-  /**
-   * Instructs the tracer actor to stop tracing.
-   */
-  stopTracing: function(aCallback = () => {}) {
-    if (!this.tracing) {
-      return;
-    }
-    this.traceClient.stopTrace(this._trace, aResponse => {
-      const { error } = aResponse;
-      if (error) {
-        DevToolsUtils.reportException("Tracer.prototype.stopTracing", error);
-      }
-
-      this._trace = null;
-      DebuggerController.HitCounts.clear();
-      aCallback(aResponse);
-    });
-  },
-
-  onTraces: function (aEvent, { traces }) {
-    const tracesLength = traces.length;
-    let tracesToShow;
-
-    // Update hit counts.
-    for (let t of traces) {
-      if (t.type == "enteredFrame") {
-        DebuggerController.HitCounts.set(t.location, t.hitCount);
-      }
-    }
-    DebuggerController.HitCounts.updateEditorHitCounts();
-
-    // Limit number of traces to be shown in the log.
-    if (tracesLength > TracerView.MAX_TRACES) {
-      tracesToShow = traces.slice(tracesLength - TracerView.MAX_TRACES, tracesLength);
-      this._stack.splice(0, this._stack.length);
-      DebuggerView.Tracer.empty();
-    } else {
-      tracesToShow = traces;
-    }
-
-    // Show traces in the log.
-    for (let t of tracesToShow) {
-      if (t.type == "enteredFrame") {
-        this._onCall(t);
-      } else {
-        this._onReturn(t);
-      }
-    }
-    DebuggerView.Tracer.commit();
-  },
-
-  /**
-   * Callback for handling a new call frame.
-   */
-  _onCall: function({ name, location, blackBoxed, parameterNames, depth, arguments: args }) {
-    const item = {
-      name: name,
-      location: location,
-      id: this._idCounter++,
-      blackBoxed
-    };
-
-    this._stack.push(item);
-    DebuggerView.Tracer.addTrace({
-      type: "call",
-      name: name,
-      location: location,
-      depth: depth,
-      parameterNames: parameterNames,
-      arguments: args,
-      frameId: item.id,
-      blackBoxed
-    });
-  },
-
-  /**
-   * Callback for handling an exited frame.
-   */
-  _onReturn: function(aPacket) {
-    if (!this._stack.length) {
-      return;
-    }
-
-    const { name, id, location, blackBoxed } = this._stack.pop();
-    DebuggerView.Tracer.addTrace({
-      type: aPacket.why,
-      name: name,
-      location: location,
-      depth: aPacket.depth,
-      frameId: id,
-      returnVal: aPacket.return || aPacket.throw || aPacket.yield,
-      blackBoxed
-    });
-  },
-
-  /**
-   * Create an object which has the same interface as a normal object client,
-   * but since we already have all the information for an object that we will
-   * ever get (the server doesn't create actors when tracing, just firehoses
-   * data and forgets about it) just return the data immdiately.
-   *
-   * @param Object aObject
-   *        The tracer object "grip" (more like a limited snapshot).
-   * @returns Object
-   *          The synchronous client object.
-   */
-  syncGripClient: function(aObject) {
-    return {
-      get isFrozen() { return aObject.frozen; },
-      get isSealed() { return aObject.sealed; },
-      get isExtensible() { return aObject.extensible; },
-
-      get ownProperties() { return aObject.ownProperties; },
-      get prototype() { return null; },
-
-      getParameterNames: callback => callback(aObject),
-      getPrototypeAndProperties: callback => callback(aObject),
-      getPrototype: callback => callback(aObject),
-
-      getOwnPropertyNames: (callback) => {
-        callback({
-          ownPropertyNames: aObject.ownProperties
-            ? Object.keys(aObject.ownProperties)
-            : []
-        });
-      },
-
-      getProperty: (property, callback) => {
-        callback({
-          descriptor: aObject.ownProperties
-            ? aObject.ownProperties[property]
-            : null
-        });
-      },
-
-      getDisplayString: callback => callback("[object " + aObject.class + "]"),
-
-      getScope: callback => callback({
-        error: "scopeNotAvailable",
-        message: "Cannot get scopes for traced objects"
-      })
-    };
-  },
-
-  /**
-   * Wraps object snapshots received from the tracer server so that we can
-   * differentiate them from long living object grips from the debugger server
-   * in the variables view.
-   *
-   * @param Object aObject
-   *        The object snapshot from the tracer actor.
-   */
-  WrappedObject: function(aObject) {
-    this.object = aObject;
-  }
-};
-
-/**
  * Handles breaking on event listeners in the currently debugged target.
  */
 function EventListeners() {
 }
 
 EventListeners.prototype = {
   /**
    * A list of event names on which the debuggee will automatically pause
@@ -2162,20 +1899,20 @@ Breakpoints.prototype = {
         if (condition) {
           aBreakpointClient = yield aBreakpointClient.setCondition(
             gThreadClient,
             condition
           );
         }
       }
 
-      // Preserve information about the breakpoint's line text, to display it
-      // in the sources pane without requiring fetching the source (for example,
-      // after the target navigated). Note that this will get out of sync
-      // if the source text contents change.
+      // Preserve information about the breakpoint's line text, to display it in
+      // the sources pane without requiring fetching the source (for example,
+      // after the target navigated). Note that this will get out of sync if the
+      // source text contents change.
       let line = aBreakpointClient.location.line - 1;
       aBreakpointClient.text = DebuggerView.editor.getText(line).trim();
 
       // Show the breakpoint in the breakpoints pane, and resolve.
       yield this._showBreakpoint(aBreakpointClient, aOptions);
 
       // Notify that we've added a breakpoint.
       window.emit(EVENTS.BREAKPOINT_ADDED, aBreakpointClient);
@@ -2423,97 +2160,16 @@ Breakpoints.prototype = {
 Object.defineProperty(Breakpoints.prototype, "_addedOrDisabled", {
   get: function* () {
     yield* this._added.values();
     yield* this._disabled.values();
   }
 });
 
 /**
- * Handles Tracer's hit counts.
- */
-function HitCounts() {
-  /**
-   * Storage of hit counts for every location
-   * hitCount = _locations[url][line][column]
-   */
-  this._hitCounts = Object.create(null);
-}
-
-HitCounts.prototype = {
-  set: function({url, line, column}, aHitCount) {
-    if (url) {
-      if (!this._hitCounts[url]) {
-        this._hitCounts[url] = Object.create(null);
-      }
-      if (!this._hitCounts[url][line]) {
-        this._hitCounts[url][line] = Object.create(null);
-      }
-      this._hitCounts[url][line][column] = aHitCount;
-    }
-  },
-
-  /**
-   * Update all the hit counts in the editor view. This is invoked when the
-   * selected script is changed, or when new sources are received via the
-   * _onNewSource and _onSourcesAdded event listeners.
-   */
-  updateEditorHitCounts: function() {
-    // First, remove all hit counters.
-    DebuggerView.editor.removeAllMarkers("hit-counts");
-
-    // Then, add new hit counts, just for the current source.
-    for (let url in this._hitCounts) {
-      for (let line in this._hitCounts[url]) {
-        for (let column in this._hitCounts[url][line]) {
-          this._updateEditorHitCount({url, line, column});
-        }
-      }
-    }
-  },
-
-  /**
-   * Update a hit counter on a certain line.
-   */
-  _updateEditorHitCount: function({url, line, column}) {
-    // Editor must be initialized.
-    if (!DebuggerView.editor) {
-      return;
-    }
-
-    // No need to do anything if the counter's source is not being shown in the
-    // editor.
-    if (url &&
-        DebuggerView.Sources.selectedItem.attachment.source.url != url) {
-      return;
-    }
-
-    // There might be more counters on the same line. We need to combine them
-    // into one.
-    let content = Object.keys(this._hitCounts[url][line])
-                    .sort() // Sort by key (column).
-                    .map(a => this._hitCounts[url][line][a]) // Extract values.
-                    .map(a => a + "\u00D7") // Format hit count (e.g. 146×).
-                    .join("|");
-
-    // CodeMirror's lines are indexed from 0, while traces start from 1
-    DebuggerView.editor.addContentMarker(line - 1, "hit-counts", "hit-count",
-                                         content);
-  },
-
-  /**
-   * Remove all hit couters and clear the storage
-   */
-  clear: function() {
-    DebuggerView.editor.removeAllMarkers("hit-counts");
-    this._hitCounts = Object.create(null);
-  }
-}
-
-/**
  * Localization convenience methods.
  */
 let L10N = new ViewHelpers.L10N(DBG_STRINGS_URI);
 
 /**
  * Shortcuts for accessing various debugger preferences.
  */
 let Prefs = new ViewHelpers.Prefs("devtools", {
@@ -2523,17 +2179,16 @@ let Prefs = new ViewHelpers.Prefs("devto
   variablesSortingEnabled: ["Bool", "debugger.ui.variables-sorting-enabled"],
   variablesOnlyEnumVisible: ["Bool", "debugger.ui.variables-only-enum-visible"],
   variablesSearchboxVisible: ["Bool", "debugger.ui.variables-searchbox-visible"],
   pauseOnExceptions: ["Bool", "debugger.pause-on-exceptions"],
   ignoreCaughtExceptions: ["Bool", "debugger.ignore-caught-exceptions"],
   sourceMapsEnabled: ["Bool", "debugger.source-maps-enabled"],
   prettyPrintEnabled: ["Bool", "debugger.pretty-print-enabled"],
   autoPrettyPrint: ["Bool", "debugger.auto-pretty-print"],
-  tracerEnabled: ["Bool", "debugger.tracer"],
   workersEnabled: ["Bool", "debugger.workers"],
   editorTabSize: ["Int", "editor.tabsize"],
   autoBlackBox: ["Bool", "debugger.auto-black-box"]
 });
 
 /**
  * Convenient way of emitting events from the panel window.
  */
@@ -2545,18 +2200,16 @@ EventEmitter.decorate(this);
 DebuggerController.initialize();
 DebuggerController.Parser = new Parser();
 DebuggerController.Workers = new Workers();
 DebuggerController.ThreadState = new ThreadState();
 DebuggerController.StackFrames = new StackFrames();
 DebuggerController.SourceScripts = new SourceScripts();
 DebuggerController.Breakpoints = new Breakpoints();
 DebuggerController.Breakpoints.DOM = new EventListeners();
-DebuggerController.Tracer = new Tracer();
-DebuggerController.HitCounts = new HitCounts();
 
 /**
  * Export some properties to the global scope for easier access.
  */
 Object.defineProperties(window, {
   "gTarget": {
     get: function() {
       return DebuggerController._target;
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -53,17 +53,16 @@ let DebuggerView = {
     this.Toolbar.initialize();
     this.Options.initialize();
     this.Filtering.initialize();
     this.StackFrames.initialize();
     this.StackFramesClassicList.initialize();
     this.Workers.initialize();
     this.Sources.initialize();
     this.VariableBubble.initialize();
-    this.Tracer.initialize();
     this.WatchExpressions.initialize();
     this.EventListeners.initialize();
     this.GlobalSearch.initialize();
     this._initializeVariablesView();
     this._initializeEditor(deferred.resolve);
 
     document.title = L10N.getStr("DebuggerWindowTitle");
 
@@ -86,17 +85,16 @@ let DebuggerView = {
 
     this.Toolbar.destroy();
     this.Options.destroy();
     this.Filtering.destroy();
     this.StackFrames.destroy();
     this.StackFramesClassicList.destroy();
     this.Sources.destroy();
     this.VariableBubble.destroy();
-    this.Tracer.destroy();
     this.WatchExpressions.destroy();
     this.EventListeners.destroy();
     this.GlobalSearch.destroy();
     this._destroyPanes();
     this._destroyEditor(deferred.resolve);
 
     return deferred.promise;
   },
@@ -169,19 +167,17 @@ let DebuggerView = {
     // Attach the current toolbox to the VView so it can link DOMNodes to
     // the inspector/highlighter
     this.Variables.toolbox = DebuggerController._toolbox;
 
     // Attach a controller that handles interfacing with the debugger protocol.
     VariablesViewController.attach(this.Variables, {
       getEnvironmentClient: aObject => gThreadClient.environment(aObject),
       getObjectClient: aObject => {
-        return aObject instanceof DebuggerController.Tracer.WrappedObject
-          ? DebuggerController.Tracer.syncGripClient(aObject.object)
-          : gThreadClient.pauseGrip(aObject)
+        return gThreadClient.pauseGrip(aObject)
       }
     });
 
     // Relay events from the VariablesView.
     this.Variables.on("fetched", (aEvent, aType) => {
       switch (aType) {
         case "scopes":
           window.emit(EVENTS.FETCHED_SCOPES);
@@ -214,19 +210,16 @@ let DebuggerView = {
 
     function bindKey(func, key, modifiers = {}) {
       key = document.getElementById(key).getAttribute("key");
       let shortcut = Editor.accel(key, modifiers);
       extraKeys[shortcut] = () => DebuggerView.Filtering[func]();
     }
 
     let gutters = ["breakpoints"];
-    if (Services.prefs.getBoolPref("devtools.debugger.tracer")) {
-      gutters.unshift("hit-counts");
-    }
 
     this.editor = new Editor({
       mode: Editor.modes.text,
       readOnly: true,
       lineNumbers: true,
       showAnnotationRuler: true,
       gutters: gutters,
       extraKeys: extraKeys,
@@ -409,17 +402,16 @@ let DebuggerView = {
 
       this._setEditorText(aText);
       this._setEditorMode(aSource.url, aContentType, aText);
 
       // Synchronize any other components with the currently displayed
       // source.
       DebuggerView.Sources.selectedValue = aSource.actor;
       DebuggerController.Breakpoints.updateEditorBreakpoints();
-      DebuggerController.HitCounts.updateEditorHitCounts();
 
       histogram.add(Date.now() - startTime);
 
       // Resolve and notify that a source file was shown.
       window.emit(EVENTS.SOURCE_SHOWN, aSource);
       deferred.resolve([aSource, aText, aContentType]);
     },
     ([, aError]) => {
@@ -668,17 +660,16 @@ let DebuggerView = {
   _startup: null,
   _shutdown: null,
   Toolbar: null,
   Options: null,
   Filtering: null,
   GlobalSearch: null,
   StackFrames: null,
   Sources: null,
-  Tracer: null,
   Variables: null,
   VariableBubble: null,
   WatchExpressions: null,
   EventListeners: null,
   editor: null,
   _editorSource: {},
   _loadingText: "",
   _body: null,
--- a/browser/devtools/debugger/debugger.xul
+++ b/browser/devtools/debugger/debugger.xul
@@ -26,26 +26,25 @@
           src="chrome://browser/content/devtools/theme-switching.js"/>
   <script type="text/javascript" src="chrome://global/content/globalOverlay.js"/>
   <script type="text/javascript" src="debugger-controller.js"/>
   <script type="text/javascript" src="debugger-view.js"/>
   <script type="text/javascript" src="debugger/utils.js"/>
   <script type="text/javascript" src="debugger/workers-view.js"/>
   <script type="text/javascript" src="debugger/sources-view.js"/>
   <script type="text/javascript" src="debugger/variable-bubble-view.js"/>
-  <script type="text/javascript" src="debugger/tracer-view.js"/>
   <script type="text/javascript" src="debugger/watch-expressions-view.js"/>
   <script type="text/javascript" src="debugger/event-listeners-view.js"/>
   <script type="text/javascript" src="debugger/global-search-view.js"/>
   <script type="text/javascript" src="debugger/toolbar-view.js"/>
   <script type="text/javascript" src="debugger/options-view.js"/>
   <script type="text/javascript" src="debugger/stack-frames-view.js"/>
   <script type="text/javascript" src="debugger/stack-frames-classic-view.js"/>
   <script type="text/javascript" src="debugger/filter-view.js"/>
-  
+
   <commandset id="editMenuCommands"/>
 
   <commandset id="debuggerCommands"></commandset>
 
   <popupset id="debuggerPopupset">
     <menupopup id="sourceEditorContextMenu"
                onpopupshowing="goUpdateGlobalEditMenuItems()">
       <menuitem id="se-dbg-cMenu-addBreakpoint"
@@ -123,26 +122,16 @@
                onpopupshowing="DebuggerView.Options._onPopupShowing()"
                onpopuphiding="DebuggerView.Options._onPopupHiding()"
                onpopuphidden="DebuggerView.Options._onPopupHidden()">
       <menuitem id="auto-pretty-print"
                 type="checkbox"
                 label="&debuggerUI.autoPrettyPrint;"
                 accesskey="&debuggerUI.autoPrettyPrint.accesskey;"
                 command="toggleAutoPrettyPrint"/>
-      <menuitem id="pause-on-exceptions"
-                type="checkbox"
-                label="&debuggerUI.pauseExceptions;"
-                accesskey="&debuggerUI.pauseExceptions.accesskey;"
-                command="togglePauseOnExceptions"/>
-      <menuitem id="ignore-caught-exceptions"
-                type="checkbox"
-                label="&debuggerUI.ignoreCaughtExceptions;"
-                accesskey="&debuggerUI.ignoreCaughtExceptions.accesskey;"
-                command="toggleIgnoreCaughtExceptions"/>
       <menuitem id="show-panes-on-startup"
                 type="checkbox"
                 label="&debuggerUI.showPanesOnInit;"
                 accesskey="&debuggerUI.showPanesOnInit.accesskey;"
                 command="toggleShowPanesOnStartup"/>
       <menuitem id="show-vars-only-enum"
                 type="checkbox"
                 label="&debuggerUI.showOnlyEnum;"
@@ -285,23 +274,16 @@
                        tabindex="0"/>
         <toolbarbutton id="step-in"
                        class="devtools-toolbarbutton"
                        tabindex="0"/>
         <toolbarbutton id="step-out"
                        class="devtools-toolbarbutton"
                        tabindex="0"/>
       </hbox>
-      <hbox>
-        <toolbarbutton id="trace"
-                       class="devtools-toolbarbutton"
-                       command="toggleTracing"
-                       tabindex="0"
-                       hidden="true"/>
-      </hbox>
       <vbox id="stackframes" flex="1"/>
       <textbox id="searchbox"
                class="devtools-searchinput" type="search"/>
       <toolbarbutton id="instruments-pane-toggle"
                      class="devtools-toolbarbutton"
                      tooltiptext="&debuggerUI.panesButton.tooltip;"
                      tabindex="0"/>
       <toolbarbutton id="debugger-options"
@@ -329,17 +311,16 @@
         </tabbox>
         <splitter class="devtools-horizontal-splitter"/>
         <tabbox id="sources-pane"
                 class="devtools-sidebar-tabs"
                 flex="1">
           <tabs>
             <tab id="sources-tab" label="&debuggerUI.tabs.sources;"/>
             <tab id="callstack-tab" label="&debuggerUI.tabs.callstack;"/>
-            <tab id="tracer-tab" label="&debuggerUI.tabs.traces;" hidden="true"/>
           </tabs>
           <tabpanels flex="1">
             <tabpanel id="sources-tabpanel">
               <vbox id="sources" flex="1"/>
               <toolbar id="sources-toolbar" class="devtools-toolbar">
                 <hbox id="sources-controls"
                       class="devtools-toolbarbutton-group">
                   <toolbarbutton id="black-box"
@@ -352,41 +333,24 @@
                                  command="prettyPrintCommand"
                                  hidden="true"/>
                 </hbox>
                 <vbox class="devtools-separator"/>
                 <toolbarbutton id="toggle-breakpoints"
                                class="devtools-toolbarbutton"
                                tooltiptext="&debuggerUI.sources.toggleBreakpoints;"
                                command="toggleBreakpointsCommand"/>
+                <toolbarbutton id="toggle-pause-exceptions"
+                               class="devtools-toolbarbutton"
+                               command="togglePauseOnExceptionsCommand"/>
               </toolbar>
             </tabpanel>
             <tabpanel id="callstack-tabpanel">
               <vbox id="callstack-list" flex="1"/>
             </tabpanel>
-            <tabpanel id="tracer-tabpanel">
-              <vbox id="tracer-traces" flex="1"/>
-              <hbox class="trace-item-template" hidden="true">
-                <hbox class="trace-item" align="center" flex="1" crop="end">
-                  <label class="trace-type plain"/>
-                  <label class="trace-name plain" crop="end"/>
-                </hbox>
-              </hbox>
-              <toolbar id="tracer-toolbar" class="devtools-toolbar">
-                <toolbarbutton id="clear-tracer"
-                               label="&debuggerUI.clearButton;"
-                               tooltiptext="&debuggerUI.clearButton.tooltip;"
-                               command="clearTraces"
-                               class="devtools-toolbarbutton"/>
-                <textbox id="tracer-search"
-                         class="devtools-searchinput"
-                         flex="1"
-                         type="search"/>
-              </toolbar>
-            </tabpanel>
           </tabpanels>
         </tabbox>
       </vbox>
       <splitter id="sources-and-editor-splitter"
                 class="devtools-side-splitter"/>
       <deck id="editor-deck" flex="1" class="devtools-main-content">
         <vbox id="editor"/>
         <vbox id="black-boxed-message"
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -28,17 +28,16 @@ support-files =
   code_math.map
   code_math.min.js
   code_math_bogus_map.js
   code_same-line-functions.js
   code_script-eval.js
   code_script-switching-01.js
   code_script-switching-02.js
   code_test-editor-mode
-  code_tracing-01.js
   code_ugly.js
   code_ugly-2.js
   code_ugly-3.js
   code_ugly-4.js
   code_ugly-5.js
   code_ugly-6.js
   code_ugly-7.js
   code_ugly-8
@@ -86,29 +85,27 @@ support-files =
   doc_pretty-print.html
   doc_pretty-print-2.html
   doc_pretty-print-3.html
   doc_pretty-print-on-paused.html
   doc_promise-get-allocation-stack.html
   doc_promise.html
   doc_random-javascript.html
   doc_recursion-stack.html
-  doc_same-line-functions.html
   doc_scope-variable.html
   doc_scope-variable-2.html
   doc_scope-variable-3.html
   doc_scope-variable-4.html
   doc_script-eval.html
   doc_script-bookmarklet.html
   doc_script-switching-01.html
   doc_script-switching-02.html
   doc_split-console-paused-reload.html
   doc_step-out.html
   doc_terminate-on-tab-close.html
-  doc_tracing-01.html
   doc_watch-expressions.html
   doc_watch-expression-button.html
   doc_with-frame.html
   doc_WorkerActor.attach-tab1.html
   doc_WorkerActor.attach-tab2.html
   doc_WorkerActor.attachThread-tab.html
   head.js
   sjs_random-javascript.sjs
@@ -242,20 +239,16 @@ skip-if = e10s && debug
 [browser_dbg_function-display-name.js]
 skip-if = e10s && debug
 [browser_dbg_global-method-override.js]
 skip-if = e10s && debug
 [browser_dbg_globalactor.js]
 skip-if = e10s # TODO
 [browser_dbg_hide-toolbar-buttons.js]
 skip-if = e10s
-[browser_dbg_hit-counts-01.js]
-skip-if = e10s && debug
-[browser_dbg_hit-counts-02.js]
-skip-if = e10s && debug
 [browser_dbg_host-layout.js]
 skip-if = e10s && debug
 [browser_dbg_iframes.js]
 skip-if = e10s # TODO
 [browser_dbg_instruments-pane-collapse.js]
 skip-if = e10s && debug
 [browser_dbg_interrupts.js]
 skip-if = e10s && debug
@@ -348,17 +341,17 @@ skip-if = e10s && debug
 skip-if = e10s && debug
 [browser_dbg_pretty-print-on-paused.js]
 skip-if = e10s && debug
 [browser_dbg_progress-listener-bug.js]
 skip-if = e10s && debug
 [browser_dbg_promises-allocation-stack.js]
 skip-if = e10s && debug
 [browser_dbg_promises-chrome-allocation-stack.js]
-skip-if = (e10s && debug) || os == "linux" # Bug 1177730
+skip-if = (e10s && debug) || os == "linux" || os == "win" # Bug 1177730
 [browser_dbg_reload-preferred-script-01.js]
 skip-if = e10s && debug
 [browser_dbg_reload-preferred-script-02.js]
 skip-if = e10s && debug
 [browser_dbg_reload-preferred-script-03.js]
 skip-if = e10s && debug
 [browser_dbg_reload-same-script.js]
 skip-if = e10s && debug
@@ -448,32 +441,16 @@ skip-if = e10s && debug
 [browser_dbg_step-out.js]
 skip-if = e10s && debug
 [browser_dbg_tabactor-01.js]
 skip-if = e10s # TODO
 [browser_dbg_tabactor-02.js]
 skip-if = e10s # TODO
 [browser_dbg_terminate-on-tab-close.js]
 skip-if = e10s && debug
-[browser_dbg_tracing-01.js]
-skip-if = e10s && debug
-[browser_dbg_tracing-02.js]
-skip-if = e10s && debug
-[browser_dbg_tracing-03.js]
-skip-if = e10s && debug
-[browser_dbg_tracing-04.js]
-skip-if = e10s && debug
-[browser_dbg_tracing-05.js]
-skip-if = e10s && debug
-[browser_dbg_tracing-06.js]
-skip-if = e10s && debug
-[browser_dbg_tracing-07.js]
-skip-if = e10s && debug
-[browser_dbg_tracing-08.js]
-skip-if = e10s && debug
 [browser_dbg_variables-view-01.js]
 skip-if = e10s && debug
 [browser_dbg_variables-view-02.js]
 skip-if = e10s && debug
 [browser_dbg_variables-view-03.js]
 skip-if = e10s && debug
 [browser_dbg_variables-view-04.js]
 skip-if = e10s && debug
deleted file mode 100644
--- a/browser/devtools/debugger/test/browser_dbg_hit-counts-01.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Evaluating two functions on the same line and checking for correct hit count
- * for both of them in CodeMirror's gutter.
- */
-
-const TAB_URL = EXAMPLE_URL + "doc_same-line-functions.html";
-const CODE_URL = "code_same-line-functions.js";
-
-let gTab, gPanel, gDebugger;
-let gEditor;
-
-function test() {
-  Task.async(function* () {
-    yield pushPrefs(["devtools.debugger.tracer", true]);
-
-    initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
-      gTab = aTab;
-      gPanel = aPanel;
-      gDebugger = gPanel.panelWin;
-      gEditor = gDebugger.DebuggerView.editor;
-
-      Task.async(function* () {
-        yield waitForSourceShown(gPanel, CODE_URL);
-        yield startTracing(gPanel);
-
-        clickButton();
-
-        yield waitForClientEvents(aPanel, "traces");
-
-        testHitCounts();
-
-        yield stopTracing(gPanel);
-        yield popPrefs();
-        yield closeDebuggerAndFinish(gPanel);
-      })();
-    });
-  })().catch(e => {
-    ok(false, "Got an error: " + e.message + "\n" + e.stack);
-  });
-}
-
-function clickButton() {
-  generateMouseClickInTab(gTab, "content.document.querySelector('button')");
-}
-
-function testHitCounts() {
-  let marker = gEditor.getMarker(0, 'hit-counts');
-
-  is(marker.innerHTML, "1\u00D7|1\u00D7",
-    "Both functions should be hit only once.");
-}
-
-registerCleanupFunction(function() {
-  gTab = null;
-  gPanel = null;
-  gDebugger = null;
-  gEditor = null;
-});
deleted file mode 100644
--- a/browser/devtools/debugger/test/browser_dbg_hit-counts-02.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * When tracing is stopped all hit counters should be cleared.
- */
-
-const TAB_URL = EXAMPLE_URL + "doc_same-line-functions.html";
-const CODE_URL = "code_same-line-functions.js";
-
-let gTab, gPanel, gDebugger;
-let gEditor;
-
-function test() {
-  Task.async(function* () {
-    yield pushPrefs(["devtools.debugger.tracer", true]);
-
-    initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
-      gTab = aTab;
-      gPanel = aPanel;
-      gDebugger = gPanel.panelWin;
-      gEditor = gDebugger.DebuggerView.editor;
-
-      Task.async(function* () {
-        yield waitForSourceShown(gPanel, CODE_URL);
-        yield startTracing(gPanel);
-
-        clickButton();
-
-        yield waitForClientEvents(aPanel, "traces");
-
-        testHitCountsBeforeStopping();
-
-        yield stopTracing(gPanel);
-
-        testHitCountsAfterStopping();
-
-        yield popPrefs();
-        yield closeDebuggerAndFinish(gPanel);
-      })();
-    });
-  })().catch(e => {
-    ok(false, "Got an error: " + e.message + "\n" + e.stack);
-  });
-}
-
-function clickButton() {
-  generateMouseClickInTab(gTab, "content.document.querySelector('button')");
-}
-
-function testHitCountsBeforeStopping() {
-  let marker = gEditor.getMarker(0, 'hit-counts');
-  ok(marker, "A counter should exists.");
-}
-
-function testHitCountsAfterStopping() {
-  let marker = gEditor.getMarker(0, 'hit-counts');
-  is(marker, undefined, "A counter should be cleared.");
-}
-
-registerCleanupFunction(function() {
-  gTab = null;
-  gPanel = null;
-  gDebugger = null;
-  gEditor = null;
-});
--- a/browser/devtools/debugger/test/browser_dbg_pause-exceptions-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_pause-exceptions-01.js
@@ -3,40 +3,37 @@
 
 /**
  * Make sure that pausing on exceptions works.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_pause-exceptions.html";
 
 let gTab, gPanel, gDebugger;
-let gFrames, gVariables, gPrefs, gOptions;
+let gFrames, gVariables, gPrefs;
 
 function test() {
   requestLongerTimeout(2);
   initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gFrames = gDebugger.DebuggerView.StackFrames;
     gVariables = gDebugger.DebuggerView.Variables;
     gPrefs = gDebugger.Prefs;
-    gOptions = gDebugger.DebuggerView.Options;
 
     is(gPrefs.pauseOnExceptions, false,
       "The pause-on-exceptions pref should be disabled by default.");
-    isnot(gOptions._pauseOnExceptionsItem.getAttribute("checked"), "true",
-      "The pause-on-exceptions menu item should not be checked.");
 
     testPauseOnExceptionsDisabled()
-      .then(enablePauseOnExceptions)
-      .then(disableIgnoreCaughtExceptions)
-      .then(testPauseOnExceptionsEnabled)
-      .then(disablePauseOnExceptions)
-      .then(enableIgnoreCaughtExceptions)
+      .then(clickToPauseOnAllExceptions)
+      .then(testPauseOnAllExceptionsEnabled)
+      .then(clickToPauseOnUncaughtExceptions)
+      .then(testPauseOnUncaughtExceptionsEnabled)
+      .then(clickToStopPauseOnExceptions)
       .then(() => closeDebuggerAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
   });
 }
 
 function testPauseOnExceptionsDisabled() {
@@ -77,19 +74,62 @@ function testPauseOnExceptionsDisabled()
     return finished;
   });
 
   generateMouseClickInTab(gTab, "content.document.querySelector('button')");
 
   return finished;
 }
 
-function testPauseOnExceptionsEnabled() {
+function testPauseOnUncaughtExceptionsEnabled() {
+  let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES).then(() => {
+    info("Testing enabled pause-on-uncaught-exceptions only.");
+
+    is(gDebugger.gThreadClient.state, "paused",
+      "Should only be getting stack frames while paused (1).");
+    ok(isCaretPos(gPanel, 26),
+      "Should be paused on the debugger statement (1).");
+
+    let innerScope = gVariables.getScopeAtIndex(0);
+    let innerNodes = innerScope.target.querySelector(".variables-view-element-details").childNodes;
+
+    is(gFrames.itemCount, 1,
+      "Should have one frame.");
+    is(gVariables._store.length, 3,
+      "Should have three scopes.");
+
+    is(innerNodes[0].querySelector(".name").getAttribute("value"), "this",
+      "Should have the right property name for 'this'.");
+    is(innerNodes[0].querySelector(".value").getAttribute("value"), "<button>",
+      "Should have the right property value for 'this'.");
+
+    let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.AFTER_FRAMES_CLEARED).then(() => {
+      isnot(gDebugger.gThreadClient.state, "paused",
+        "Should not be paused after resuming.");
+      ok(isCaretPos(gPanel, 26),
+        "Should be idle on the debugger statement.");
+
+      ok(true, "Frames were cleared, debugger didn't pause again.");
+    });
+
+    EventUtils.sendMouseEvent({ type: "mousedown" },
+      gDebugger.document.getElementById("resume"),
+      gDebugger);
+
+    return finished;
+  });
+
+  generateMouseClickInTab(gTab, "content.document.querySelector('button')");
+
+  return finished;
+}
+
+function testPauseOnAllExceptionsEnabled() {
   let finished = waitForCaretAndScopes(gPanel, 19).then(() => {
-    info("Testing enabled pause-on-exceptions.");
+    info("Testing enabled pause-on-all-exceptions.");
 
     is(gDebugger.gThreadClient.state, "paused",
       "Should only be getting stack frames while paused.");
     ok(isCaretPos(gPanel, 19),
       "Should be paused on the debugger statement.");
 
     let innerScope = gVariables.getScopeAtIndex(0);
     let innerNodes = innerScope.target.querySelector(".variables-view-element-details").childNodes;
@@ -100,17 +140,17 @@ function testPauseOnExceptionsEnabled() 
       "Should have three scopes.");
 
     is(innerNodes[0].querySelector(".name").getAttribute("value"), "<exception>",
       "Should have the right property name for <exception>.");
     is(innerNodes[0].querySelector(".value").getAttribute("value"), "Error",
       "Should have the right property value for <exception>.");
 
     let finished = waitForCaretAndScopes(gPanel, 26).then(() => {
-      info("Testing enabled pause-on-exceptions and resumed after pause.");
+      info("Testing enabled pause-on-all-exceptions and resumed after pause.");
 
       is(gDebugger.gThreadClient.state, "paused",
         "Should only be getting stack frames while paused.");
       ok(isCaretPos(gPanel, 26),
         "Should be paused on the debugger statement.");
 
       let innerScope = gVariables.getScopeAtIndex(0);
       let innerNodes = innerScope.target.querySelector(".variables-view-element-details").childNodes;
@@ -148,93 +188,74 @@ function testPauseOnExceptionsEnabled() 
     return finished;
   });
 
   generateMouseClickInTab(gTab, "content.document.querySelector('button')");
 
   return finished;
 }
 
-function enablePauseOnExceptions() {
-  let deferred = promise.defer();
+function clickToPauseOnAllExceptions() {
+  var deferred = promise.defer();
+  var pauseOnExceptionsButton = getPauseOnExceptionsButton();
 
   gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
-    is(gPrefs.pauseOnExceptions, true,
-      "The pause-on-exceptions pref should now be enabled.");
-    is(gOptions._pauseOnExceptionsItem.getAttribute("checked"), "true",
-      "The pause-on-exceptions menu item should now be checked.");
+    is(pauseOnExceptionsButton.getAttribute("tooltiptext"),
+      "Pause on uncaught exceptions",
+      "The button's tooltip text should be 'Pause on uncaught exceptions'.");
+    is(pauseOnExceptionsButton.getAttribute("state"), 1,
+      "The pause on exceptions button state variable should be 1");
 
-    ok(true, "Pausing on exceptions was enabled.");
-    deferred.resolve();
+      deferred.resolve();
   });
 
-  gOptions._pauseOnExceptionsItem.setAttribute("checked", "true");
-  gOptions._togglePauseOnExceptions();
-
-  return deferred.promise;
-}
-
-function disablePauseOnExceptions() {
-  let deferred = promise.defer();
-
-  gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
-    is(gPrefs.pauseOnExceptions, false,
-      "The pause-on-exceptions pref should now be disabled.");
-    isnot(gOptions._pauseOnExceptionsItem.getAttribute("checked"), "true",
-      "The pause-on-exceptions menu item should now be unchecked.");
-
-    ok(true, "Pausing on exceptions was disabled.");
-    deferred.resolve();
-  });
-
-  gOptions._pauseOnExceptionsItem.setAttribute("checked", "false");
-  gOptions._togglePauseOnExceptions();
+  pauseOnExceptionsButton.click();
 
   return deferred.promise;
 }
 
-function enableIgnoreCaughtExceptions() {
-  let deferred = promise.defer();
+function clickToPauseOnUncaughtExceptions() {
+  var deferred = promise.defer();
+  var pauseOnExceptionsButton = getPauseOnExceptionsButton();
 
-  gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
-    is(gPrefs.ignoreCaughtExceptions, true,
-      "The ignore-caught-exceptions pref should now be enabled.");
-    is(gOptions._ignoreCaughtExceptionsItem.getAttribute("checked"), "true",
-      "The ignore-caught-exceptions menu item should now be checked.");
+  gDebugger.gThreadClient.addOneTimeListener("resumed", () =>{
+    is(pauseOnExceptionsButton.getAttribute("tooltiptext"),
+      "Do not pause on exceptions",
+      "The button's tooltip text should be 'Do not pause on exceptions'.");
+    is(pauseOnExceptionsButton.getAttribute("state"), 2,
+      "The pause on exceptions button state variable should be 2");
 
-    ok(true, "Ignore caught exceptions was enabled.");
-    deferred.resolve();
+      deferred.resolve();
   });
-
-  gOptions._ignoreCaughtExceptionsItem.setAttribute("checked", "true");
-  gOptions._toggleIgnoreCaughtExceptions();
-
+  pauseOnExceptionsButton.click();
   return deferred.promise;
 }
 
-function disableIgnoreCaughtExceptions() {
-  let deferred = promise.defer();
+function clickToStopPauseOnExceptions() {
+  var deferred = promise.defer();
+  var pauseOnExceptionsButton = getPauseOnExceptionsButton();
 
-  gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
-    is(gPrefs.ignoreCaughtExceptions, false,
-      "The ignore-caught-exceptions pref should now be disabled.");
-    isnot(gOptions._ignoreCaughtExceptionsItem.getAttribute("checked"), "true",
-      "The ignore-caught-exceptions menu item should now be unchecked.");
+  gDebugger.gThreadClient.addOneTimeListener("resumed", () =>{
+    is(pauseOnExceptionsButton.getAttribute("tooltiptext"),
+      "Pause on all exceptions",
+      "The button's tooltip text should be 'Pause on all exceptions'.");
+    is(pauseOnExceptionsButton.getAttribute("state"), 0,
+      "The pause on exceptions button state variable should be 0");
 
-    ok(true, "Ignore caught exceptions was disabled.");
-    deferred.resolve();
+      deferred.resolve();
   });
-
-  gOptions._ignoreCaughtExceptionsItem.setAttribute("checked", "false");
-  gOptions._toggleIgnoreCaughtExceptions();
-
+  pauseOnExceptionsButton.click();
   return deferred.promise;
 }
 
+function getPauseOnExceptionsButton() {
+  return gDebugger.document.getElementById("toggle-pause-exceptions");
+}
+
+
 registerCleanupFunction(function() {
   gTab = null;
   gPanel = null;
   gDebugger = null;
   gFrames = null;
   gVariables = null;
   gPrefs = null;
-  gOptions = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_pause-exceptions-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_pause-exceptions-02.js
@@ -1,51 +1,48 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Make sure that pausing on exceptions works after reload.
+ * Make sure that pausing on exceptions works after tab reload.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_pause-exceptions.html";
 
 let gTab, gPanel, gDebugger;
-let gFrames, gVariables, gPrefs, gOptions;
+let gFrames, gVariables, gPrefs;
 
 function test() {
+  requestLongerTimeout(2);
   initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gFrames = gDebugger.DebuggerView.StackFrames;
     gVariables = gDebugger.DebuggerView.Variables;
     gPrefs = gDebugger.Prefs;
-    gOptions = gDebugger.DebuggerView.Options;
 
     is(gPrefs.pauseOnExceptions, false,
       "The pause-on-exceptions pref should be disabled by default.");
-    isnot(gOptions._pauseOnExceptionsItem.getAttribute("checked"), "true",
-      "The pause-on-exceptions menu item should not be checked.");
 
-    enablePauseOnExceptions()
-      .then(disableIgnoreCaughtExceptions)
+    clickToPauseOnAllExceptions()
       .then(() => reloadActiveTab(gPanel, gDebugger.EVENTS.SOURCE_SHOWN))
-      .then(testPauseOnExceptionsAfterReload)
-      .then(disablePauseOnExceptions)
-      .then(enableIgnoreCaughtExceptions)
+      .then(testPauseOnAllExceptionsEnabledAfterReload)
+      .then(clickToPauseOnUncaughtExceptions)
+      .then(clickToStopPauseOnExceptions)
       .then(() => closeDebuggerAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
   });
 }
 
-function testPauseOnExceptionsAfterReload() {
+function testPauseOnAllExceptionsEnabledAfterReload() {
   let finished = waitForCaretAndScopes(gPanel, 19).then(() => {
-    info("Testing enabled pause-on-exceptions.");
+    info("Testing enabled pause-on-all-exceptions.");
 
     is(gDebugger.gThreadClient.state, "paused",
       "Should only be getting stack frames while paused.");
     ok(isCaretPos(gPanel, 19),
       "Should be paused on the debugger statement.");
 
     let innerScope = gVariables.getScopeAtIndex(0);
     let innerNodes = innerScope.target.querySelector(".variables-view-element-details").childNodes;
@@ -56,17 +53,17 @@ function testPauseOnExceptionsAfterReloa
       "Should have three scopes.");
 
     is(innerNodes[0].querySelector(".name").getAttribute("value"), "<exception>",
       "Should have the right property name for <exception>.");
     is(innerNodes[0].querySelector(".value").getAttribute("value"), "Error",
       "Should have the right property value for <exception>.");
 
     let finished = waitForCaretAndScopes(gPanel, 26).then(() => {
-      info("Testing enabled pause-on-exceptions and resumed after pause.");
+      info("Testing enabled pause-on-all-exceptions and resumed after pause.");
 
       is(gDebugger.gThreadClient.state, "paused",
         "Should only be getting stack frames while paused.");
       ok(isCaretPos(gPanel, 26),
         "Should be paused on the debugger statement.");
 
       let innerScope = gVariables.getScopeAtIndex(0);
       let innerNodes = innerScope.target.querySelector(".variables-view-element-details").childNodes;
@@ -104,93 +101,73 @@ function testPauseOnExceptionsAfterReloa
     return finished;
   });
 
   generateMouseClickInTab(gTab, "content.document.querySelector('button')");
 
   return finished;
 }
 
-function enablePauseOnExceptions() {
-  let deferred = promise.defer();
+function clickToPauseOnAllExceptions() {
+  var deferred = promise.defer();
+  var pauseOnExceptionsButton = getPauseOnExceptionsButton();
 
   gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
-    is(gPrefs.pauseOnExceptions, true,
-      "The pause-on-exceptions pref should now be enabled.");
-    is(gOptions._pauseOnExceptionsItem.getAttribute("checked"), "true",
-      "The pause-on-exceptions menu item should now be checked.");
+    is(pauseOnExceptionsButton.getAttribute("tooltiptext"),
+      "Pause on uncaught exceptions",
+      "The button's tooltip text should be 'Pause on uncaught exceptions'.");
+    is(pauseOnExceptionsButton.getAttribute("state"), 1,
+      "The pause on exceptions button state variable should be 1");
 
-    ok(true, "Pausing on exceptions was enabled.");
-    deferred.resolve();
+      deferred.resolve();
   });
 
-  gOptions._pauseOnExceptionsItem.setAttribute("checked", "true");
-  gOptions._togglePauseOnExceptions();
+  pauseOnExceptionsButton.click();
 
   return deferred.promise;
 }
 
-function disablePauseOnExceptions() {
-  let deferred = promise.defer();
+function clickToPauseOnUncaughtExceptions() {
+  var deferred = promise.defer();
+  var pauseOnExceptionsButton = getPauseOnExceptionsButton();
 
-  gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
-    is(gPrefs.pauseOnExceptions, false,
-      "The pause-on-exceptions pref should now be disabled.");
-    isnot(gOptions._pauseOnExceptionsItem.getAttribute("checked"), "true",
-      "The pause-on-exceptions menu item should now be unchecked.");
+  gDebugger.gThreadClient.addOneTimeListener("resumed", () =>{
+    is(pauseOnExceptionsButton.getAttribute("tooltiptext"),
+      "Do not pause on exceptions",
+      "The button's tooltip text should be 'Do not pause on exceptions'.");
+    is(pauseOnExceptionsButton.getAttribute("state"), 2,
+      "The pause on exceptions button state variable should be 2");
 
-    ok(true, "Pausing on exceptions was disabled.");
-    deferred.resolve();
+      deferred.resolve();
   });
-
-  gOptions._pauseOnExceptionsItem.setAttribute("checked", "false");
-  gOptions._togglePauseOnExceptions();
-
+  pauseOnExceptionsButton.click();
   return deferred.promise;
 }
 
-function enableIgnoreCaughtExceptions() {
-  let deferred = promise.defer();
+function clickToStopPauseOnExceptions() {
+  var deferred = promise.defer();
+  var pauseOnExceptionsButton = getPauseOnExceptionsButton();
 
-  gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
-    is(gPrefs.ignoreCaughtExceptions, true,
-      "The ignore-caught-exceptions pref should now be enabled.");
-    is(gOptions._ignoreCaughtExceptionsItem.getAttribute("checked"), "true",
-      "The ignore-caught-exceptions menu item should now be checked.");
+  gDebugger.gThreadClient.addOneTimeListener("resumed", () =>{
+    is(pauseOnExceptionsButton.getAttribute("tooltiptext"),
+      "Pause on all exceptions",
+      "The button's tooltip text should be 'Pause on all exceptions'.");
+    is(pauseOnExceptionsButton.getAttribute("state"), 0,
+      "The pause on exceptions button state variable should be 0");
 
-    ok(true, "Ignore caught exceptions was enabled.");
-    deferred.resolve();
+      deferred.resolve();
   });
-
-  gOptions._ignoreCaughtExceptionsItem.setAttribute("checked", "true");
-  gOptions._toggleIgnoreCaughtExceptions();
-
+  pauseOnExceptionsButton.click();
   return deferred.promise;
 }
 
-function disableIgnoreCaughtExceptions() {
-  let deferred = promise.defer();
-
-  gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
-    is(gPrefs.ignoreCaughtExceptions, false,
-      "The ignore-caught-exceptions pref should now be disabled.");
-    isnot(gOptions._ignoreCaughtExceptionsItem.getAttribute("checked"), "true",
-      "The ignore-caught-exceptions menu item should now be unchecked.");
-
-    ok(true, "Ignore caught exceptions was disabled.");
-    deferred.resolve();
-  });
-
-  gOptions._ignoreCaughtExceptionsItem.setAttribute("checked", "false");
-  gOptions._toggleIgnoreCaughtExceptions();
-
-  return deferred.promise;
+function getPauseOnExceptionsButton() {
+  return gDebugger.document.getElementById("toggle-pause-exceptions");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
   gPanel = null;
   gDebugger = null;
   gFrames = null;
   gVariables = null;
   gPrefs = null;
-  gOptions = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_source-maps-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_source-maps-04.js
@@ -8,85 +8,104 @@
 const TAB_URL = EXAMPLE_URL + "doc_minified_bogus_map.html";
 const JS_URL = EXAMPLE_URL + "code_math_bogus_map.js";
 
 // This test causes an error to be logged in the console, which appears in TBPL
 // logs, so we are disabling that here.
 let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
 DevToolsUtils.reportingDisabled = true;
 
-let gPanel, gDebugger, gFrames, gSources, gPrefs, gOptions;
+let gPanel, gDebugger, gFrames, gSources, gPrefs;
 
 function test() {
   initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gFrames = gDebugger.DebuggerView.StackFrames;
     gSources = gDebugger.DebuggerView.Sources;
     gPrefs = gDebugger.Prefs;
-    gOptions = gDebugger.DebuggerView.Options;
 
     is(gPrefs.pauseOnExceptions, false,
       "The pause-on-exceptions pref should be disabled by default.");
-    isnot(gOptions._pauseOnExceptionsItem.getAttribute("checked"), "true",
-      "The pause-on-exceptions menu item should not be checked.");
 
     waitForSourceShown(gPanel, JS_URL)
       .then(checkInitialSource)
-      .then(enablePauseOnExceptions)
-      .then(disableIgnoreCaughtExceptions)
+      .then(clickToPauseOnAllExceptions)
+      .then(clickToPauseOnUncaughtExceptions)
       .then(testSetBreakpoint)
       .then(reloadPage)
       .then(testHitBreakpoint)
-      .then(enableIgnoreCaughtExceptions)
-      .then(disablePauseOnExceptions)
+      .then(clickToStopPauseOnExceptions)
       .then(() => closeDebuggerAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
   });
 }
 
 function checkInitialSource() {
   isnot(gSources.selectedItem.attachment.source.url.indexOf("code_math_bogus_map.js"), -1,
     "The debugger should show the minified js file.");
 }
 
-function enablePauseOnExceptions() {
-  let deferred = promise.defer();
+function clickToPauseOnAllExceptions() {
+  var deferred = promise.defer();
+  var pauseOnExceptionsButton = getPauseOnExceptionsButton();
 
   gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
-    is(gPrefs.pauseOnExceptions, true,
-      "The pause-on-exceptions pref should now be enabled.");
+    is(pauseOnExceptionsButton.getAttribute("tooltiptext"),
+      "Pause on uncaught exceptions",
+      "The button's tooltip text should be 'Pause on uncaught exceptions'.");
+    is(pauseOnExceptionsButton.getAttribute("state"), 1,
+      "The pause on exceptions button state variable should be 1");
 
-    ok(true, "Pausing on exceptions was enabled.");
-    deferred.resolve();
+      deferred.resolve();
   });
 
-  gOptions._pauseOnExceptionsItem.setAttribute("checked", "true");
-  gOptions._togglePauseOnExceptions();
+  pauseOnExceptionsButton.click();
 
   return deferred.promise;
 }
 
-function disableIgnoreCaughtExceptions() {
-  let deferred = promise.defer();
+function clickToPauseOnUncaughtExceptions() {
+  var deferred = promise.defer();
+  var pauseOnExceptionsButton = getPauseOnExceptionsButton();
+
+  gDebugger.gThreadClient.addOneTimeListener("resumed", () =>{
+    is(pauseOnExceptionsButton.getAttribute("tooltiptext"),
+      "Do not pause on exceptions",
+      "The button's tooltip text should be 'Do not pause on exceptions'.");
+    is(pauseOnExceptionsButton.getAttribute("state"), 2,
+      "The pause on exceptions button state variable should be 2");
+
+      deferred.resolve();
+  });
+  pauseOnExceptionsButton.click();
+  return deferred.promise;
+}
+
+function clickToStopPauseOnExceptions() {
+  var deferred = promise.defer();
+  var pauseOnExceptionsButton = getPauseOnExceptionsButton();
 
   gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
-    is(gPrefs.ignoreCaughtExceptions, false,
-      "The ignore-caught-exceptions pref should now be disabled.");
+    is(pauseOnExceptionsButton.getAttribute("tooltiptext"),
+      "Pause on all exceptions",
+      "The button's tooltip text should be 'Pause on all exceptions'.");
+    is(pauseOnExceptionsButton.getAttribute("state"), 0,
+      "The pause on exceptions button state variable should be 0");
 
-    ok(true, "Ignore caught exceptions was disabled.");
-    deferred.resolve();
+      deferred.resolve();
   });
+  pauseOnExceptionsButton.click();
+  return deferred.promise;
+}
 
-  gOptions._ignoreCaughtExceptionsItem.setAttribute("checked", "false");
-  gOptions._toggleIgnoreCaughtExceptions();
-
-  return deferred.promise;
+function getPauseOnExceptionsButton() {
+  return gDebugger.document.getElementById("toggle-pause-exceptions");
 }
 
 function testSetBreakpoint() {
   let deferred = promise.defer();
   let sourceForm = getSourceForm(gSources, JS_URL);
   let source = gDebugger.gThreadClient.source(sourceForm);
 
   source.setBreakpoint({ line: 3, column: 18 }, aResponse => {
@@ -108,76 +127,24 @@ function reloadPage() {
 }
 
 function testHitBreakpoint() {
   let deferred = promise.defer();
 
   gDebugger.gThreadClient.resume(aResponse => {
     ok(!aResponse.error, "Shouldn't get an error resuming.");
     is(aResponse.type, "resumed", "Type should be 'resumed'.");
-
-    waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES).then(() => {
-      is(gFrames.itemCount, 2, "Should have two frames.");
+    is(gFrames.itemCount, 2, "Should have two frames.");
 
-      // This is weird, but we need to let the debugger a chance to
-      // update first
-      executeSoon(() => {
-        gDebugger.gThreadClient.resume(() => {
-          gDebugger.gThreadClient.addOneTimeListener("paused", () => {
-            gDebugger.gThreadClient.resume(() => {
-              // We also need to make sure the next step doesn't add a
-              // "resumed" handler until this is completely finished
-              executeSoon(() => {
-                deferred.resolve();
-              });
-            });
-          });
-        });
-      });
-    });
-  });
-
-  return deferred.promise;
-}
-
-function enableIgnoreCaughtExceptions() {
-  let deferred = promise.defer();
-
-  gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
-    is(gPrefs.ignoreCaughtExceptions, true,
-      "The ignore-caught-exceptions pref should now be enabled.");
-
-    ok(true, "Ignore caught exceptions was enabled.");
     deferred.resolve();
   });
 
-  gOptions._ignoreCaughtExceptionsItem.setAttribute("checked", "true");
-  gOptions._toggleIgnoreCaughtExceptions();
-
-  return deferred.promise;
-}
-
-function disablePauseOnExceptions() {
-  let deferred = promise.defer();
-
-  gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
-    is(gPrefs.pauseOnExceptions, false,
-      "The pause-on-exceptions pref should now be disabled.");
-
-    ok(true, "Pausing on exceptions was disabled.");
-    deferred.resolve();
-  });
-
-  gOptions._pauseOnExceptionsItem.setAttribute("checked", "false");
-  gOptions._togglePauseOnExceptions();
-
   return deferred.promise;
 }
 
 registerCleanupFunction(function() {
   gPanel = null;
   gDebugger = null;
   gFrames = null;
   gSources = null;
   gPrefs = null;
-  gOptions = null;
   DevToolsUtils.reportingDisabled = false;
 });
deleted file mode 100644
--- a/browser/devtools/debugger/test/browser_dbg_tracing-01.js
+++ /dev/null
@@ -1,105 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Test that we get the expected frame enter/exit logs in the tracer view.
- */
-
-const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
-
-let gTab, gPanel, gDebugger;
-
-function test() {
-  SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
-    initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
-      gTab = aTab;
-      gPanel = aPanel;
-      gDebugger = gPanel.panelWin;
-
-      waitForSourceShown(gPanel, "code_tracing-01.js")
-        .then(() => startTracing(gPanel))
-        .then(clickButton)
-        .then(() => waitForClientEvents(aPanel, "traces"))
-        .then(testTraceLogs)
-        .then(() => stopTracing(gPanel))
-        .then(() => {
-          const deferred = promise.defer();
-          SpecialPowers.popPrefEnv(deferred.resolve);
-          return deferred.promise;
-        })
-        .then(() => closeDebuggerAndFinish(gPanel))
-        .then(null, aError => {
-          ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-        });
-    });
-  });
-}
-
-function clickButton() {
-  generateMouseClickInTab(gTab, "content.document.querySelector('button')");
-}
-
-function testTraceLogs() {
-  const onclickLogs = filterTraces(gPanel,
-                                   t => t.querySelector(".trace-name[value=onclick]"));
-  is(onclickLogs.length, 2, "Should have two logs from 'onclick'");
-  ok(onclickLogs[0].querySelector(".trace-call"),
-     "The first 'onclick' log should be a call.");
-  ok(onclickLogs[1].querySelector(".trace-return"),
-     "The second 'onclick' log should be a return.");
-  for (let t of onclickLogs) {
-    ok(t.querySelector(".trace-item").getAttribute("tooltiptext")
-        .includes("doc_tracing-01.html"));
-  }
-
-  const nonOnclickLogs = filterTraces(gPanel,
-                                      t => !t.querySelector(".trace-name[value=onclick]"));
-  for (let t of nonOnclickLogs) {
-    ok(t.querySelector(".trace-item").getAttribute("tooltiptext")
-        .includes("code_tracing-01.js"));
-  }
-
-  const mainLogs = filterTraces(gPanel,
-                                t => t.querySelector(".trace-name[value=main]"));
-  is(mainLogs.length, 2, "Should have an enter and an exit for 'main'");
-  ok(mainLogs[0].querySelector(".trace-call"),
-     "The first 'main' log should be a call.");
-  ok(mainLogs[1].querySelector(".trace-return"),
-     "The second 'main' log should be a return.");
-
-  const factorialLogs = filterTraces(gPanel,
-                                     t => t.querySelector(".trace-name[value=factorial]"));
-  is(factorialLogs.length, 10, "Should have 5 enter, and 5 exit frames for 'factorial'");
-  ok(factorialLogs.slice(0, 5).every(t => t.querySelector(".trace-call")),
-     "The first five 'factorial' logs should be calls.");
-  ok(factorialLogs.slice(5).every(t => t.querySelector(".trace-return")),
-     "The second five 'factorial' logs should be returns.")
-
-  // Test that the depth affects padding so that calls are indented properly.
-  let lastDepth = -Infinity;
-  for (let t of factorialLogs.slice(0, 5)) {
-    let depth = parseInt(t.querySelector(".trace-item").style.MozPaddingStart, 10);
-    ok(depth > lastDepth, "The depth should be increasing");
-    lastDepth = depth;
-  }
-  lastDepth = Infinity;
-  for (let t of factorialLogs.slice(5)) {
-    let depth = parseInt(t.querySelector(".trace-item").style.MozPaddingStart, 10);
-    ok(depth < lastDepth, "The depth should be decreasing");
-    lastDepth = depth;
-  }
-
-  const throwerLogs = filterTraces(gPanel,
-                                   t => t.querySelector(".trace-name[value=thrower]"));
-  is(throwerLogs.length, 2, "Should have an enter and an exit for 'thrower'");
-  ok(throwerLogs[0].querySelector(".trace-call"),
-     "The first 'thrower' log should be a call.");
-  ok(throwerLogs[1].querySelector(".trace-throw",
-     "The second 'thrower' log should be a throw."));
-}
-
-registerCleanupFunction(function() {
-  gTab = null;
-  gPanel = null;
-  gDebugger = null;
-});
deleted file mode 100644
--- a/browser/devtools/debugger/test/browser_dbg_tracing-02.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Test that we highlight matching calls and returns on hover.
- */
-
-const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
-
-let gTab, gPanel, gDebugger;
-
-function test() {
-  SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
-    initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
-      gTab = aTab;
-      gPanel = aPanel;
-      gDebugger = gPanel.panelWin;
-
-      waitForSourceShown(gPanel, "code_tracing-01.js")
-        .then(() => startTracing(gPanel))
-        .then(clickButton)
-        .then(() => waitForClientEvents(aPanel, "traces"))
-        .then(highlightCall)
-        .then(testReturnHighlighted)
-        .then(unhighlightCall)
-        .then(testNoneHighlighted)
-        .then(() => stopTracing(gPanel))
-        .then(() => {
-          const deferred = promise.defer();
-          SpecialPowers.popPrefEnv(deferred.resolve);
-          return deferred.promise;
-        })
-        .then(() => closeDebuggerAndFinish(gPanel))
-        .then(null, aError => {
-          ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-        });
-    });
-  });
-}
-
-function clickButton() {
-  generateMouseClickInTab(gTab, "content.document.querySelector('button')");
-}
-
-function highlightCall() {
-  const callTrace = filterTraces(gPanel, t => t.querySelector(".trace-name[value=main]"))[0];
-  EventUtils.sendMouseEvent({ type: "mouseover" },
-                            callTrace,
-                            gDebugger);
-}
-
-function testReturnHighlighted() {
-  const returnTrace = filterTraces(gPanel, t => t.querySelector(".trace-name[value=main]"))[1];
-  ok(Array.indexOf(returnTrace.querySelector(".trace-item").classList, "selected-matching") >= 0,
-     "The corresponding return log should be highlighted.");
-}
-
-function unhighlightCall() {
-  const callTrace = filterTraces(gPanel, t => t.querySelector(".trace-name[value=main]"))[0];
-  EventUtils.sendMouseEvent({ type: "mouseout" },
-                            callTrace,
-                            gDebugger);
-}
-
-function testNoneHighlighted() {
-  const highlightedTraces = filterTraces(gPanel, t => t.querySelector(".selected-matching"));
-  is(highlightedTraces.length, 0, "Shouldn't have any highlighted traces");
-}
-
-registerCleanupFunction(function() {
-  gTab = null;
-  gPanel = null;
-  gDebugger = null;
-});
deleted file mode 100644
--- a/browser/devtools/debugger/test/browser_dbg_tracing-03.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-SimpleTest.requestCompleteLog();
-
-/**
- * Test that we can jump to function definitions by clicking on logs.
- */
-
-const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
-
-let gTab, gPanel, gDebugger, gSources;
-
-function test() {
-  SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
-    initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
-      gTab = aTab;
-      gPanel = aPanel;
-      gDebugger = gPanel.panelWin;
-      gSources = gDebugger.DebuggerView.Sources;
-
-      waitForSourceShown(gPanel, "code_tracing-01.js")
-        .then(() => startTracing(gPanel))
-        .then(() => clickButton())
-        .then(() => waitForClientEvents(aPanel, "traces"))
-        .then(() => {
-          // Switch away from the JS file so we can make sure that clicking on a
-          // log will switch us back to the correct JS file.
-          gSources.selectedValue = getSourceActor(gSources, TAB_URL);
-          return ensureSourceIs(aPanel, getSourceActor(gSources, TAB_URL), true);
-        })
-        .then(() => {
-          const finished = waitForSourceShown(gPanel, "code_tracing-01.js");
-          clickTraceLog();
-          return finished;
-        })
-        .then(testCorrectLine)
-        .then(() => stopTracing(gPanel))
-        .then(() => {
-          const deferred = promise.defer();
-          SpecialPowers.popPrefEnv(deferred.resolve);
-          return deferred.promise;
-        })
-        .then(() => closeDebuggerAndFinish(gPanel))
-        .then(null, aError => {
-          ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-        });
-    });
-  });
-}
-
-function clickButton() {
-  generateMouseClickInTab(gTab, "content.document.querySelector('button')");
-}
-
-function clickTraceLog() {
-  filterTraces(gPanel, t => t.querySelector(".trace-name[value=main]"))[0].click();
-}
-
-function testCorrectLine() {
-  is(gDebugger.DebuggerView.editor.getCursor().line, 18,
-     "The editor should have the function definition site's line selected.");
-}
-
-registerCleanupFunction(function() {
-  gTab = null;
-  gPanel = null;
-  gDebugger = null;
-  gSources = null;
-});
deleted file mode 100644
--- a/browser/devtools/debugger/test/browser_dbg_tracing-04.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Test that when we click on logs, we get the parameters/return value in the variables view.
- */
-
-const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
-
-let gTab, gPanel, gDebugger;
-
-function test() {
-  SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
-    initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
-      gTab = aTab;
-      gPanel = aPanel;
-      gDebugger = gPanel.panelWin;
-
-      waitForSourceShown(gPanel, "code_tracing-01.js")
-        .then(() => startTracing(gPanel))
-        .then(clickButton)
-        .then(() => waitForClientEvents(aPanel, "traces"))
-        .then(clickTraceCall)
-        .then(testParams)
-        .then(clickTraceReturn)
-        .then(testReturn)
-        .then(() => stopTracing(gPanel))
-        .then(() => {
-          const deferred = promise.defer();
-          SpecialPowers.popPrefEnv(deferred.resolve);
-          return deferred.promise;
-        })
-        .then(() => closeDebuggerAndFinish(gPanel))
-        .then(null, aError => {
-          DevToolsUtils.reportException("browser_dbg_tracing-04.js", aError);
-          ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-        });
-    });
-  });
-}
-
-function clickButton() {
-  generateMouseClickInTab(gTab, "content.document.querySelector('button')");
-}
-
-function clickTraceCall() {
-  filterTraces(gPanel, t => t.querySelector(".trace-name[value=factorial]"))[0]
-    .click();
-}
-
-function testParams() {
-  const name = gDebugger.document.querySelector(".variables-view-variable .name");
-  ok(name, "Should have a variable name");
-  is(name.getAttribute("value"), "n", "The variable name should be n");
-
-  const value = gDebugger.document.querySelector(".variables-view-variable .value.token-number");
-  ok(value, "Should have a variable value");
-  is(value.getAttribute("value"), "5", "The variable value should be 5");
-}
-
-function clickTraceReturn() {
-  filterTraces(gPanel, t => t.querySelector(".trace-name[value=factorial]"))
-    .pop().click();
-}
-
-function testReturn() {
-  const name = gDebugger.document.querySelector(".variables-view-variable .name");
-  ok(name, "Should have a variable name");
-  is(name.getAttribute("value"), "<return>", "The variable name should be <return>");
-
-  const value = gDebugger.document.querySelector(".variables-view-variable .value.token-number");
-  ok(value, "Should have a variable value");
-  is(value.getAttribute("value"), "120", "The variable value should be 120");
-}
-
-registerCleanupFunction(function() {
-  gTab = null;
-  gPanel = null;
-  gDebugger = null;
-});
deleted file mode 100644
--- a/browser/devtools/debugger/test/browser_dbg_tracing-05.js
+++ /dev/null
@@ -1,81 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Test that text describing the tracing state is correctly displayed.
- */
-
-const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
-
-let gTab, gPanel, gDebugger;
-let gTracer, gL10N;
-
-function test() {
-  SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
-    initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
-      gTab = aTab;
-      gPanel = aPanel;
-      gDebugger = gPanel.panelWin;
-      gTracer = gDebugger.DebuggerView.Tracer;
-      gL10N = gDebugger.L10N;
-
-      waitForSourceShown(gPanel, "code_tracing-01.js")
-        .then(testTracingNotStartedText)
-        .then(() => gTracer._onStartTracing())
-        .then(testFunctionCallsUnavailableText)
-        .then(clickButton)
-        .then(() => waitForClientEvents(aPanel, "traces"))
-        .then(testNoEmptyText)
-        .then(() => gTracer._onClear())
-        .then(testFunctionCallsUnavailableText)
-        .then(() => gTracer._onStopTracing())
-        .then(testTracingNotStartedText)
-        .then(() => gTracer._onClear())
-        .then(testTracingNotStartedText)
-        .then(() => {
-          const deferred = promise.defer();
-          SpecialPowers.popPrefEnv(deferred.resolve);
-          return deferred.promise;
-        })
-        .then(() => closeDebuggerAndFinish(gPanel))
-        .then(null, aError => {
-          DevToolsUtils.reportException("browser_dbg_tracing-05.js", aError);
-          ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-        });
-    });
-  });
-}
-
-function testTracingNotStartedText() {
-  let label = gDebugger.document.querySelector("#tracer-tabpanel .fast-list-widget-empty-text");
-  ok(label,
-    "A label is displayed in the tracer tabpanel.");
-  is(label.getAttribute("value"), gL10N.getStr("tracingNotStartedText"),
-    "The correct {{tracingNotStartedText}} is displayed in the tracer tabpanel.");
-}
-
-function testFunctionCallsUnavailableText() {
-  let label = gDebugger.document.querySelector("#tracer-tabpanel .fast-list-widget-empty-text");
-  ok(label,
-    "A label is displayed in the tracer tabpanel.");
-  is(label.getAttribute("value"), gL10N.getStr("noFunctionCallsText"),
-    "The correct {{noFunctionCallsText}} is displayed in the tracer tabpanel.");
-}
-
-function testNoEmptyText() {
-  let label = gDebugger.document.querySelector("#tracer-tabpanel .fast-list-widget-empty-text");
-  ok(!label,
-    "No label should be displayed in the tracer tabpanel.");
-}
-
-function clickButton() {
-  generateMouseClickInTab(gTab, "content.document.querySelector('button')");
-}
-
-registerCleanupFunction(function() {
-  gTab = null;
-  gPanel = null;
-  gDebugger = null;
-  gTracer = null;
-  gL10N = null;
-});
deleted file mode 100644
--- a/browser/devtools/debugger/test/browser_dbg_tracing-06.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Test that the tracer doesn't connect to the backend when tracing is disabled.
- */
-
-const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
-const TRACER_PREF = "devtools.debugger.tracer";
-
-let gTab, gPanel, gDebugger;
-let gOriginalPref = Services.prefs.getBoolPref(TRACER_PREF);
-Services.prefs.setBoolPref(TRACER_PREF, false);
-
-function test() {
-  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
-    gTab = aTab;
-    gPanel = aPanel;
-    gDebugger = gPanel.panelWin;
-
-    waitForSourceShown(gPanel, "code_tracing-01.js")
-      .then(() => {
-        ok(!gDebugger.DebuggerController.traceClient, "Should not have a trace client");
-        closeDebuggerAndFinish(gPanel);
-      })
-      .then(null, aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-      });
-  });
-}
-
-registerCleanupFunction(function() {
-  gTab = null;
-  gPanel = null;
-  gDebugger = null;
-  Services.prefs.setBoolPref(TRACER_PREF, gOriginalPref);
-});
deleted file mode 100644
--- a/browser/devtools/debugger/test/browser_dbg_tracing-07.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Execute code both before and after blackboxing and test that we get
- * appropriately styled traces.
- */
-
-const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
-
-let gTab, gPanel;
-
-function test() {
-  Task.async(function*() {
-    yield pushPref();
-
-    [gTab,, gPanel] = yield initDebugger(TAB_URL);
-
-    yield startTracing(gPanel);
-    yield clickButton();
-    yield waitForClientEvents(gPanel, "traces");
-
-    /**
-     * Test that there are some traces which are not blackboxed.
-     */
-    const firstBbButton = getBlackBoxButton(gPanel);
-    ok(!firstBbButton.checked, "Should not be black boxed by default");
-
-    const blackBoxedTraces =
-      gPanel.panelWin.document.querySelectorAll(".trace-item.black-boxed");
-    ok(blackBoxedTraces.length === 0, "There should no blackboxed traces.");
-
-    const notBlackBoxedTraces =
-      gPanel.panelWin.document.querySelectorAll(".trace-item:not(.black-boxed)");
-    ok(notBlackBoxedTraces.length > 0,
-      "There should be some traces which are not blackboxed.");
-
-    yield toggleBlackBoxing(gPanel);
-    yield clickButton();
-    yield waitForClientEvents(gPanel, "traces");
-
-    /**
-     * Test that there are some traces which are blackboxed.
-     */
-    const secondBbButton = getBlackBoxButton(gPanel);
-    ok(secondBbButton.checked, "The checkbox should no longer be checked.");
-    const traces =
-      gPanel.panelWin.document.querySelectorAll(".trace-item.black-boxed");
-    ok(traces.length > 0, "There should be some blackboxed traces.");
-
-    yield stopTracing(gPanel);
-    yield popPref();
-    yield closeDebuggerAndFinish(gPanel);
-
-    finish();
-  })().catch(e => {
-    ok(false, "Got an error: " + e.message + "\n" + e.stack);
-    finish();
-  });
-}
-
-function clickButton() {
-  generateMouseClickInTab(gTab, "content.document.querySelector('button')");
-}
-
-function pushPref() {
-  let deferred = promise.defer();
-  SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]},
-    deferred.resolve);
-  return deferred.promise;
-}
-
-function popPref() {
-  let deferred = promise.defer();
-  SpecialPowers.popPrefEnv(deferred.resolve);
-  return deferred.promise;
-}
-
-registerCleanupFunction(function() {
-  gTab = null;
-  gPanel = null;
-});
-
deleted file mode 100644
--- a/browser/devtools/debugger/test/browser_dbg_tracing-08.js
+++ /dev/null
@@ -1,59 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Test that tracing about:config doesn't produce errors.
- */
-
-const TAB_URL = "about:config";
-
-let gPanel, gDoneChecks;
-
-function test() {
-  gDoneChecks = promise.defer();
-  const tracerPref = promise.defer();
-  const configPref = promise.defer();
-  SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, tracerPref.resolve);
-  SpecialPowers.pushPrefEnv({'set': [["general.warnOnAboutConfig", false]]}, configPref.resolve);
-  promise.all([tracerPref.promise, configPref.promise]).then(() => {
-    initDebugger(TAB_URL).then(([,, aPanel]) => {
-      gPanel = aPanel;
-      gPanel.panelWin.gClient.addOneTimeListener("traces", testTraceLogs);
-    }).then(() => startTracing(gPanel))
-      .then(generateTrace)
-      .then(() => waitForClientEvents(gPanel, "traces"))
-      .then(() => gDoneChecks.promise)
-      .then(() => stopTracing(gPanel))
-      .then(resetPreferences)
-      .then(() => closeDebuggerAndFinish(gPanel))
-      .then(null, aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-      });
-  });
-}
-
-function testTraceLogs(name, packet) {
-  info("Traces: " + packet.traces.length);
-  ok(packet.traces.length > 0, "Got some traces.");
-  ok(packet.traces.every(t => t.type != "enteredFrame" || !!t.location),
-     "All enteredFrame traces contain location.");
-  gDoneChecks.resolve();
-}
-
-function generateTrace(name, packet) {
-  // Interact with the page to cause JS execution.
-  let search = content.document.getElementById("textbox");
-  info("Interacting with the page.");
-  search.value = "devtools";
-}
-
-function resetPreferences() {
-  const deferred = promise.defer();
-  SpecialPowers.popPrefEnv(() => SpecialPowers.popPrefEnv(deferred.resolve));
-  return deferred.promise;
-}
-
-registerCleanupFunction(function() {
-  gPanel = null;
-  gDoneChecks = null;
-});
deleted file mode 100644
--- a/browser/devtools/debugger/test/code_tracing-01.js
+++ /dev/null
@@ -1,29 +0,0 @@
-function factorial(n) {
-  if (n <= 1) {
-    return 1;
-  } else {
-    return n * factorial(n - 1);
-  }
-}
-
-function* yielder(n) {
-  while (n-- >= 0) {
-    yield { value: n, squared: n * n };
-  }
-}
-
-function thrower() {
-  throw new Error("Curse your sudden but inevitable betrayal!");
-}
-
-function main() {
-  factorial(5);
-
-  // XXX bug 923729: Can't test yielding yet.
-  // for (let x of yielder(5)) {}
-
-  try {
-    thrower();
-  } catch (e) {
-  }
-}
deleted file mode 100644
--- a/browser/devtools/debugger/test/doc_same-line-functions.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!-- Any copyright is dedicated to the Public Domain.
-     http://creativecommons.org/publicdomain/zero/1.0/ -->
-<!doctype html>
-
-<html>
-  <head>
-    <meta charset="utf-8"/>
-    <title>Debugger Tracer test page</title>
-  </head>
-
-  <body>
-    <script src="code_same-line-functions.js"></script>
-    <button onclick="first()">Click me!</button>
-  </body>
-</html>
deleted file mode 100644
--- a/browser/devtools/debugger/test/doc_tracing-01.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8"/>
-    <title>Debugger Tracer test page</title>
-  </head>
-
-  <body>
-    <script src="code_tracing-01.js"></script>
-    <button onclick="main()">Click me!</button>
-
-    <script type="text/javascript">
-      // Have an inline script to make sure the HTML file is listed
-      // in the sources. We want to switch between them.
-      function foo() {
-        return 5;
-      }
-    </script>
-  </body>
-</html>
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -883,49 +883,16 @@ function hideVarPopupByScrollingEditor(a
   editor.setFirstVisibleLine(0);
   return popupHiding.then(waitForTick);
 }
 
 function reopenVarPopup(...aArgs) {
   return hideVarPopup.apply(this, aArgs).then(() => openVarPopup.apply(this, aArgs));
 }
 
-// Tracing helpers
-
-function startTracing(aPanel) {
-  const deferred = promise.defer();
-  aPanel.panelWin.DebuggerController.Tracer.startTracing(aResponse => {
-    if (aResponse.error) {
-      deferred.reject(aResponse);
-    } else {
-      deferred.resolve(aResponse);
-    }
-  });
-  return deferred.promise;
-}
-
-function stopTracing(aPanel) {
-  const deferred = promise.defer();
-  aPanel.panelWin.DebuggerController.Tracer.stopTracing(aResponse => {
-    if (aResponse.error) {
-      deferred.reject(aResponse);
-    } else {
-      deferred.resolve(aResponse);
-    }
-  });
-  return deferred.promise;
-}
-
-function filterTraces(aPanel, f) {
-  const traces = aPanel.panelWin.document
-    .getElementById("tracer-traces")
-    .querySelector("scrollbox")
-    .children;
-  return Array.filter(traces, f);
-}
 function attachAddonActorForUrl(aClient, aUrl) {
   let deferred = promise.defer();
 
   getAddonActorForUrl(aClient, aUrl).then(aGrip => {
     aClient.attachAddon(aGrip.actor, aResponse => {
       deferred.resolve([aGrip, aResponse]);
     });
   });
--- a/browser/devtools/debugger/views/options-view.js
+++ b/browser/devtools/debugger/views/options-view.js
@@ -12,45 +12,39 @@ const POPUP_HIDDEN_DELAY = 100; // ms
  */
 function OptionsView(DebuggerController, DebuggerView) {
   dumpn("OptionsView was instantiated");
 
   this.DebuggerController = DebuggerController;
   this.DebuggerView = DebuggerView;
 
   this._toggleAutoPrettyPrint = this._toggleAutoPrettyPrint.bind(this);
-  this._togglePauseOnExceptions = this._togglePauseOnExceptions.bind(this);
-  this._toggleIgnoreCaughtExceptions = this._toggleIgnoreCaughtExceptions.bind(this);
   this._toggleShowPanesOnStartup = this._toggleShowPanesOnStartup.bind(this);
   this._toggleShowVariablesOnlyEnum = this._toggleShowVariablesOnlyEnum.bind(this);
   this._toggleShowVariablesFilterBox = this._toggleShowVariablesFilterBox.bind(this);
   this._toggleShowOriginalSource = this._toggleShowOriginalSource.bind(this);
   this._toggleAutoBlackBox = this._toggleAutoBlackBox.bind(this);
 }
 
 OptionsView.prototype = {
   /**
    * Initialization function, called when the debugger is started.
    */
   initialize: function() {
     dumpn("Initializing the OptionsView");
 
     this._button = document.getElementById("debugger-options");
     this._autoPrettyPrint = document.getElementById("auto-pretty-print");
-    this._pauseOnExceptionsItem = document.getElementById("pause-on-exceptions");
-    this._ignoreCaughtExceptionsItem = document.getElementById("ignore-caught-exceptions");
     this._showPanesOnStartupItem = document.getElementById("show-panes-on-startup");
     this._showVariablesOnlyEnumItem = document.getElementById("show-vars-only-enum");
     this._showVariablesFilterBoxItem = document.getElementById("show-vars-filter-box");
     this._showOriginalSourceItem = document.getElementById("show-original-source");
     this._autoBlackBoxItem = document.getElementById("auto-black-box");
 
     this._autoPrettyPrint.setAttribute("checked", Prefs.autoPrettyPrint);
-    this._pauseOnExceptionsItem.setAttribute("checked", Prefs.pauseOnExceptions);
-    this._ignoreCaughtExceptionsItem.setAttribute("checked", Prefs.ignoreCaughtExceptions);
     this._showPanesOnStartupItem.setAttribute("checked", Prefs.panesVisibleOnStartup);
     this._showVariablesOnlyEnumItem.setAttribute("checked", Prefs.variablesOnlyEnumVisible);
     this._showVariablesFilterBoxItem.setAttribute("checked", Prefs.variablesSearchboxVisible);
     this._showOriginalSourceItem.setAttribute("checked", Prefs.sourceMapsEnabled);
     this._autoBlackBoxItem.setAttribute("checked", Prefs.autoBlackBox);
 
     this._addCommands();
   },
@@ -64,18 +58,16 @@ OptionsView.prototype = {
   },
 
   /**
    * Add commands that XUL can fire.
    */
   _addCommands: function() {
     XULUtils.addCommands(document.getElementById('debuggerCommands'), {
       toggleAutoPrettyPrint: () => this._toggleAutoPrettyPrint(),
-      togglePauseOnExceptions: () => this._togglePauseOnExceptions(),
-      toggleIgnoreCaughtExceptions: () => this._toggleIgnoreCaughtExceptions(),
       toggleShowPanesOnStartup: () => this._toggleShowPanesOnStartup(),
       toggleShowOnlyEnum: () => this._toggleShowVariablesOnlyEnum(),
       toggleShowVariablesFilterBox: () => this._toggleShowVariablesFilterBox(),
       toggleShowOriginalSource: () => this._toggleShowOriginalSource(),
       toggleAutoBlackBox: () =>  this._toggleAutoBlackBox()
     });
   },
 
@@ -105,37 +97,16 @@ OptionsView.prototype = {
    * Listener handling the 'auto pretty print' menuitem command.
    */
   _toggleAutoPrettyPrint: function(){
     Prefs.autoPrettyPrint =
       this._autoPrettyPrint.getAttribute("checked") == "true";
   },
 
   /**
-   * Listener handling the 'pause on exceptions' menuitem command.
-   */
-  _togglePauseOnExceptions: function() {
-    Prefs.pauseOnExceptions =
-      this._pauseOnExceptionsItem.getAttribute("checked") == "true";
-
-    this.DebuggerController.activeThread.pauseOnExceptions(
-      Prefs.pauseOnExceptions,
-      Prefs.ignoreCaughtExceptions);
-  },
-
-  _toggleIgnoreCaughtExceptions: function() {
-    Prefs.ignoreCaughtExceptions =
-      this._ignoreCaughtExceptionsItem.getAttribute("checked") == "true";
-
-    this.DebuggerController.activeThread.pauseOnExceptions(
-      Prefs.pauseOnExceptions,
-      Prefs.ignoreCaughtExceptions);
-  },
-
-  /**
    * Listener handling the 'show panes on startup' menuitem command.
    */
   _toggleShowPanesOnStartup: function() {
     Prefs.panesVisibleOnStartup =
       this._showPanesOnStartupItem.getAttribute("checked") == "true";
   },
 
   /**
@@ -193,17 +164,16 @@ OptionsView.prototype = {
           useSourceMaps: Prefs.sourceMapsEnabled,
           autoBlackBox: pref
         });
       }, POPUP_HIDDEN_DELAY);
     });
   },
 
   _button: null,
-  _pauseOnExceptionsItem: null,
   _showPanesOnStartupItem: null,
   _showVariablesOnlyEnumItem: null,
   _showVariablesFilterBoxItem: null,
   _showOriginalSourceItem: null,
   _autoBlackBoxItem: null
 };
 
 DebuggerView.Options = new OptionsView(DebuggerController, DebuggerView);
--- a/browser/devtools/debugger/views/sources-view.js
+++ b/browser/devtools/debugger/views/sources-view.js
@@ -3,32 +3,37 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // Maps known URLs to friendly source group names and put them at the
 // bottom of source list.
 const KNOWN_SOURCE_GROUPS = {
   "Add-on SDK": "resource://gre/modules/commonjs/",
 };
+const DO_NOT_PAUSE_ON_EXCEPTIONS = 0;
+const PAUSE_ON_ALL_EXCEPTIONS = 1;
+const PAUSE_ON_UNCAUGHT_EXCEPTIONS = 2;
 
 KNOWN_SOURCE_GROUPS[L10N.getStr("anonymousSourcesLabel")] = "anonymous";
 
 /**
  * Functions handling the sources UI.
  */
 function SourcesView(DebuggerController, DebuggerView) {
   dumpn("SourcesView was instantiated");
 
   this.Breakpoints = DebuggerController.Breakpoints;
   this.SourceScripts = DebuggerController.SourceScripts;
   this.DebuggerView = DebuggerView;
+  this.DebuggerController = DebuggerController;
 
   this.togglePrettyPrint = this.togglePrettyPrint.bind(this);
   this.toggleBlackBoxing = this.toggleBlackBoxing.bind(this);
   this.toggleBreakpoints = this.toggleBreakpoints.bind(this);
+  this.togglePauseOnExceptions = this.togglePauseOnExceptions.bind(this);
 
   this._onEditorLoad = this._onEditorLoad.bind(this);
   this._onEditorUnload = this._onEditorUnload.bind(this);
   this._onEditorCursorActivity = this._onEditorCursorActivity.bind(this);
   this._onSourceSelect = this._onSourceSelect.bind(this);
   this._onStopBlackBoxing = this._onStopBlackBoxing.bind(this);
   this._onBreakpointRemoved = this._onBreakpointRemoved.bind(this);
   this._onBreakpointClick = this._onBreakpointClick.bind(this);
@@ -51,33 +56,40 @@ SourcesView.prototype = Heritage.extend(
     this.widget = new SideMenuWidget(document.getElementById("sources"), {
       contextMenu: document.getElementById("debuggerSourcesContextMenu"),
       showArrows: true
     });
 
     this._unnamedSourceIndex = 0;
     this.emptyText = L10N.getStr("noSourcesText");
     this._blackBoxCheckboxTooltip = L10N.getStr("blackBoxCheckboxTooltip");
+    this._pauseAllExceptionsTooltip = L10N.getStr("pauseAllExceptionsTooltip");
+    this._pauseUncaughtExceptionsTooltip = L10N.getStr("pauseUncaughtExceptionsTooltip");
+    this._pauseNoExceptionsTooltip = L10N.getStr("pauseNoExceptionsTooltip");
 
     this._commandset = document.getElementById("debuggerCommands");
     this._popupset = document.getElementById("debuggerPopupset");
     this._cmPopup = document.getElementById("sourceEditorContextMenu");
     this._cbPanel = document.getElementById("conditional-breakpoint-panel");
     this._cbTextbox = document.getElementById("conditional-breakpoint-panel-textbox");
     this._blackBoxButton = document.getElementById("black-box");
     this._stopBlackBoxButton = document.getElementById("black-boxed-message-button");
     this._prettyPrintButton = document.getElementById("pretty-print");
     this._toggleBreakpointsButton = document.getElementById("toggle-breakpoints");
+    this._togglePauseOnExceptionsButton = document.getElementById("toggle-pause-exceptions");
     this._newTabMenuItem = document.getElementById("debugger-sources-context-newtab");
     this._copyUrlMenuItem = document.getElementById("debugger-sources-context-copyurl");
 
     if (Prefs.prettyPrintEnabled) {
       this._prettyPrintButton.removeAttribute("hidden");
     }
 
+    this._togglePauseOnExceptionsButton.setAttribute("tooltiptext", this._pauseAllExceptionsTooltip);
+    this._togglePauseOnExceptionsButton.setAttribute("state", DO_NOT_PAUSE_ON_EXCEPTIONS);
+
     window.on(EVENTS.EDITOR_LOADED, this._onEditorLoad, false);
     window.on(EVENTS.EDITOR_UNLOADED, this._onEditorUnload, false);
     this.widget.addEventListener("select", this._onSourceSelect, false);
     this._stopBlackBoxButton.addEventListener("click", this._onStopBlackBoxing, false);
     this._cbPanel.addEventListener("popupshowing", this._onConditionalPopupShowing, false);
     this._cbPanel.addEventListener("popupshown", this._onConditionalPopupShown, false);
     this._cbPanel.addEventListener("popuphiding", this._onConditionalPopupHiding, false);
     this._cbTextbox.addEventListener("keypress", this._onConditionalTextboxKeyPress, false);
@@ -133,16 +145,17 @@ SourcesView.prototype = Heritage.extend(
   _addCommands: function() {
     XULUtils.addCommands(this._commandset, {
       addBreakpointCommand: e => this._onCmdAddBreakpoint(e),
       addConditionalBreakpointCommand: e => this._onCmdAddConditionalBreakpoint(e),
       blackBoxCommand: () => this.toggleBlackBoxing(),
       unBlackBoxButton: () => this._onStopBlackBoxing(),
       prettyPrintCommand: () => this.togglePrettyPrint(),
       toggleBreakpointsCommand: () =>this.toggleBreakpoints(),
+      togglePauseOnExceptionsCommand: () => this.togglePauseOnExceptions(),
       nextSourceCommand: () => this.selectNextItem(),
       prevSourceCommand: () => this.selectPrevItem()
     });
   },
 
   /**
    * Sets the preferred location to be selected in this sources container.
    * @param string aUrl
@@ -610,16 +623,47 @@ SourcesView.prototype = Heritage.extend(
       this._toggleBreakpointsButton.setAttribute("checked", true);
       this._onDisableAll();
     } else {
       this._toggleBreakpointsButton.removeAttribute("checked");
       this._onEnableAll();
     }
   },
 
+  /**
+   * Toggles the pause on exceptions functionality
+   */
+  togglePauseOnExceptions: function() {
+    let state = Number(this._togglePauseOnExceptionsButton.getAttribute("state"));
+    let tooltip;
+
+    state = ++state % 3;
+
+    if (state === DO_NOT_PAUSE_ON_EXCEPTIONS) {
+      tooltip = this._pauseAllExceptionsTooltip;
+      Prefs.pauseOnExceptions = false;
+      Prefs.ignoreCaughtExceptions = false;
+    } else if ( state === PAUSE_ON_ALL_EXCEPTIONS) {
+      tooltip = this._pauseUncaughtExceptionsTooltip;
+      Prefs.pauseOnExceptions = true;
+      Prefs.ignoreCaughtExceptions = false;
+    } else {
+      tooltip = this._pauseNoExceptionsTooltip;
+      Prefs.pauseOnExceptions = true;
+      Prefs.ignoreCaughtExceptions = true;
+    }
+
+    this.DebuggerController.activeThread.pauseOnExceptions(
+      Prefs.pauseOnExceptions,
+      Prefs.ignoreCaughtExceptions);
+
+    this._togglePauseOnExceptionsButton.setAttribute("tooltiptext", tooltip);
+    this._togglePauseOnExceptionsButton.setAttribute("state", state);
+  },
+
   hidePrettyPrinting: function() {
     this._prettyPrintButton.style.display = 'none';
 
     if (this._blackBoxButton.style.display === 'none') {
       let sep = document.querySelector('#sources-toolbar .devtools-separator');
       sep.style.display = 'none';
     }
   },
--- a/browser/devtools/debugger/views/stack-frames-view.js
+++ b/browser/devtools/debugger/views/stack-frames-view.js
@@ -238,12 +238,13 @@ StackFramesView.prototype = Heritage.ext
       this.dirty = false;
 
       // Loads more stack frames from the debugger server cache.
       DebuggerController.StackFrames.addMoreFrames();
     }
   },
 
   _mirror: null,
-  _prevBlackBoxedUrl: null
+  _prevBlackBoxedUrl: null,
+  _popupset: null
 });
 
 DebuggerView.StackFrames = new StackFramesView(DebuggerController, DebuggerView);
deleted file mode 100644
--- a/browser/devtools/debugger/views/tracer-view.js
+++ /dev/null
@@ -1,416 +0,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/. */
-"use strict";
-
-/**
- * Functions handling the traces UI.
- */
-function TracerView(DebuggerController, DebuggerView) {
-  this._selectedItem = null;
-  this._matchingItems = null;
-  this.widget = null;
-
-  this.Tracer = DebuggerController.Tracer;
-  this.DebuggerView = DebuggerView;
-
-  this._highlightItem = this._highlightItem.bind(this);
-  this._isNotSelectedItem = this._isNotSelectedItem.bind(this);
-
-  this._unhighlightMatchingItems =
-    DevToolsUtils.makeInfallible(this._unhighlightMatchingItems.bind(this));
-  this._onToggleTracing =
-    DevToolsUtils.makeInfallible(this._onToggleTracing.bind(this));
-  this._onStartTracing =
-    DevToolsUtils.makeInfallible(this._onStartTracing.bind(this));
-  this._onClear =
-    DevToolsUtils.makeInfallible(this._onClear.bind(this));
-  this._onSelect =
-    DevToolsUtils.makeInfallible(this._onSelect.bind(this));
-  this._onMouseOver =
-    DevToolsUtils.makeInfallible(this._onMouseOver.bind(this));
-  this._onSearch =
-    DevToolsUtils.makeInfallible(this._onSearch.bind(this));
-}
-
-TracerView.MAX_TRACES = 200;
-
-TracerView.prototype = Heritage.extend(WidgetMethods, {
-  /**
-   * Initialization function, called when the debugger is started.
-   */
-  initialize: function() {
-    dumpn("Initializing the TracerView");
-
-    this._traceButton = document.getElementById("trace");
-    this._tracerTab = document.getElementById("tracer-tab");
-
-    // Remove tracer related elements from the dom and tear everything down if
-    // the tracer isn't enabled.
-    if (!Prefs.tracerEnabled) {
-      this._traceButton.remove();
-      this._traceButton = null;
-      this._tracerTab.remove();
-      this._tracerTab = null;
-      return;
-    }
-
-    this.widget = new FastListWidget(document.getElementById("tracer-traces"));
-    this._traceButton.removeAttribute("hidden");
-    this._tracerTab.removeAttribute("hidden");
-
-    this._search = document.getElementById("tracer-search");
-    this._template = document.getElementsByClassName("trace-item-template")[0];
-    this._templateItem = this._template.getElementsByClassName("trace-item")[0];
-    this._templateTypeIcon = this._template.getElementsByClassName("trace-type")[0];
-    this._templateNameNode = this._template.getElementsByClassName("trace-name")[0];
-
-    this.widget.addEventListener("select", this._onSelect, false);
-    this.widget.addEventListener("mouseover", this._onMouseOver, false);
-    this.widget.addEventListener("mouseout", this._unhighlightMatchingItems, false);
-    this._search.addEventListener("input", this._onSearch, false);
-
-    this._startTooltip = L10N.getStr("startTracingTooltip");
-    this._stopTooltip = L10N.getStr("stopTracingTooltip");
-    this._tracingNotStartedString = L10N.getStr("tracingNotStartedText");
-    this._noFunctionCallsString = L10N.getStr("noFunctionCallsText");
-
-    this._traceButton.setAttribute("tooltiptext", this._startTooltip);
-    this.emptyText = this._tracingNotStartedString;
-
-    this._addCommands();
-  },
-
-  /**
-   * Destruction function, called when the debugger is closed.
-   */
-  destroy: function() {
-    dumpn("Destroying the TracerView");
-
-    if (!this.widget) {
-      return;
-    }
-
-    this.widget.removeEventListener("select", this._onSelect, false);
-    this.widget.removeEventListener("mouseover", this._onMouseOver, false);
-    this.widget.removeEventListener("mouseout", this._unhighlightMatchingItems, false);
-    this._search.removeEventListener("input", this._onSearch, false);
-  },
-
-  /**
-   * Add commands that XUL can fire.
-   */
-  _addCommands: function() {
-    XULUtils.addCommands(document.getElementById('debuggerCommands'), {
-      toggleTracing: () => this._onToggleTracing(),
-      startTracing: () => this._onStartTracing(),
-      clearTraces: () => this._onClear()
-    });
-  },
-
-  /**
-   * Function invoked by the "toggleTracing" command to switch the tracer state.
-   */
-  _onToggleTracing: function() {
-    if (this.Tracer.tracing) {
-      this._onStopTracing();
-    } else {
-      this._onStartTracing();
-    }
-  },
-
-  /**
-   * Function invoked either by the "startTracing" command or by
-   * _onToggleTracing to start execution tracing in the backend.
-   *
-   * @return object
-   *         A promise resolved once the tracing has successfully started.
-   */
-  _onStartTracing: function() {
-    this._traceButton.setAttribute("checked", true);
-    this._traceButton.setAttribute("tooltiptext", this._stopTooltip);
-
-    this.empty();
-    this.emptyText = this._noFunctionCallsString;
-
-    let deferred = promise.defer();
-    this.Tracer.startTracing(deferred.resolve);
-    return deferred.promise;
-  },
-
-  /**
-   * Function invoked by _onToggleTracing to stop execution tracing in the
-   * backend.
-   *
-   * @return object
-   *         A promise resolved once the tracing has successfully stopped.
-   */
-  _onStopTracing: function() {
-    this._traceButton.removeAttribute("checked");
-    this._traceButton.setAttribute("tooltiptext", this._startTooltip);
-
-    this.emptyText = this._tracingNotStartedString;
-
-    let deferred = promise.defer();
-    this.Tracer.stopTracing(deferred.resolve);
-    return deferred.promise;
-  },
-
-  /**
-   * Function invoked by the "clearTraces" command to empty the traces pane.
-   */
-  _onClear: function() {
-    this.empty();
-  },
-
-  /**
-   * Populate the given parent scope with the variable with the provided name
-   * and value.
-   *
-   * @param String aName
-   *        The name of the variable.
-   * @param Object aParent
-   *        The parent scope.
-   * @param Object aValue
-   *        The value of the variable.
-   */
-  _populateVariable: function(aName, aParent, aValue) {
-    let item = aParent.addItem(aName, { value: aValue });
-
-    if (aValue) {
-      let wrappedValue = new this.Tracer.WrappedObject(aValue);
-      this.DebuggerView.Variables.controller.populate(item, wrappedValue);
-      item.expand();
-      item.twisty = false;
-    }
-  },
-
-  /**
-   * Handler for the widget's "select" event. Displays parameters, exception, or
-   * return value depending on whether the selected trace is a call, throw, or
-   * return respectively.
-   *
-   * @param Object traceItem
-   *        The selected trace item.
-   */
-  _onSelect: function _onSelect({ detail: traceItem }) {
-    if (!traceItem) {
-      return;
-    }
-
-    const data = traceItem.attachment.trace;
-    const { location: { url, line } } = data;
-    this.DebuggerView.setEditorLocation(
-      this.DebuggerView.Sources.getActorForLocation({ url }),
-      line,
-      { noDebug: true }
-    );
-
-    this.DebuggerView.Variables.empty();
-    const scope = this.DebuggerView.Variables.addScope();
-
-    if (data.type == "call") {
-      const params = DevToolsUtils.zip(data.parameterNames, data.arguments);
-      for (let [name, val] of params) {
-        if (val === undefined) {
-          scope.addItem(name, { value: "<value not available>" });
-        } else {
-          this._populateVariable(name, scope, val);
-        }
-      }
-    } else {
-      const varName = "<" + (data.type == "throw" ? "exception" : data.type) + ">";
-      this._populateVariable(varName, scope, data.returnVal);
-    }
-
-    scope.expand();
-    this.DebuggerView.showInstrumentsPane();
-  },
-
-  /**
-   * Add the hover frame enter/exit highlighting to a given item.
-   */
-  _highlightItem: function(aItem) {
-    if (!aItem || !aItem.target) {
-      return;
-    }
-    const trace = aItem.target.querySelector(".trace-item");
-    trace.classList.add("selected-matching");
-  },
-
-  /**
-   * Remove the hover frame enter/exit highlighting to a given item.
-   */
-  _unhighlightItem: function(aItem) {
-    if (!aItem || !aItem.target) {
-      return;
-    }
-    const match = aItem.target.querySelector(".selected-matching");
-    if (match) {
-      match.classList.remove("selected-matching");
-    }
-  },
-
-  /**
-   * Remove the frame enter/exit pair highlighting we do when hovering.
-   */
-  _unhighlightMatchingItems: function() {
-    if (this._matchingItems) {
-      this._matchingItems.forEach(this._unhighlightItem);
-      this._matchingItems = null;
-    }
-  },
-
-  /**
-   * Returns true if the given item is not the selected item.
-   */
-  _isNotSelectedItem: function(aItem) {
-    return aItem !== this.selectedItem;
-  },
-
-  /**
-   * Highlight the frame enter/exit pair of items for the given item.
-   */
-  _highlightMatchingItems: function(aItem) {
-    const frameId = aItem.attachment.trace.frameId;
-    const predicate = e => e.attachment.trace.frameId == frameId;
-
-    this._unhighlightMatchingItems();
-    this._matchingItems = this.items.filter(predicate);
-    this._matchingItems
-      .filter(this._isNotSelectedItem)
-      .forEach(this._highlightItem);
-  },
-
-  /**
-   * Listener for the mouseover event.
-   */
-  _onMouseOver: function({ target }) {
-    const traceItem = this.getItemForElement(target);
-    if (traceItem) {
-      this._highlightMatchingItems(traceItem);
-    }
-  },
-
-  /**
-   * Listener for typing in the search box.
-   */
-  _onSearch: function() {
-    const query = this._search.value.trim().toLowerCase();
-    const predicate = name => name.toLowerCase().includes(query);
-    this.filterContents(item => predicate(item.attachment.trace.name));
-  },
-
-  /**
-   * Select the traces tab in the sidebar.
-   */
-  selectTab: function() {
-    const tabs = this._tracerTab.parentElement;
-    tabs.selectedIndex = Array.indexOf(tabs.children, this._tracerTab);
-  },
-
-  /**
-   * Commit all staged items to the widget. Overridden so that we can call
-   * |FastListWidget.prototype.flush|.
-   */
-  commit: function() {
-    WidgetMethods.commit.call(this);
-    // TODO: Accessing non-standard widget properties. Figure out what's the
-    // best way to expose such things. Bug 895514.
-    this.widget.flush();
-  },
-
-  /**
-   * Adds the trace record provided as an argument to the view.
-   *
-   * @param object aTrace
-   *        The trace record coming from the tracer actor.
-   */
-  addTrace: function(aTrace) {
-    // Create the element node for the trace item.
-    let view = this._createView(aTrace);
-
-    // Append a source item to this container.
-    this.push([view], {
-      staged: true,
-      attachment: {
-        trace: aTrace
-      }
-    });
-  },
-
-  /**
-   * Customization function for creating an item's UI.
-   *
-   * @return nsIDOMNode
-   *         The network request view.
-   */
-  _createView: function(aTrace) {
-    let { type, name, location, blackBoxed, depth, frameId } = aTrace;
-    let { parameterNames, returnVal, arguments: args } = aTrace;
-    let fragment = document.createDocumentFragment();
-
-    this._templateItem.classList.toggle("black-boxed", blackBoxed);
-    this._templateItem.setAttribute("tooltiptext", SourceUtils.trimUrl(location.url));
-    this._templateItem.style.MozPaddingStart = depth + "em";
-
-    const TYPES = ["call", "yield", "return", "throw"];
-    for (let t of TYPES) {
-      this._templateTypeIcon.classList.toggle("trace-" + t, t == type);
-    }
-    this._templateTypeIcon.setAttribute("value", {
-      call: "\u2192",
-      yield: "Y",
-      return: "\u2190",
-      throw: "E",
-      terminated: "TERMINATED"
-    }[type]);
-
-    this._templateNameNode.setAttribute("value", name);
-
-    // All extra syntax and parameter nodes added.
-    const addedNodes = [];
-
-    if (parameterNames) {
-      const syntax = (p) => {
-        const el = document.createElement("label");
-        el.setAttribute("value", p);
-        el.classList.add("trace-syntax");
-        el.classList.add("plain");
-        addedNodes.push(el);
-        return el;
-      };
-
-      this._templateItem.appendChild(syntax("("));
-
-      for (let i = 0, n = parameterNames.length; i < n; i++) {
-        let param = document.createElement("label");
-        param.setAttribute("value", parameterNames[i]);
-        param.classList.add("trace-param");
-        param.classList.add("plain");
-        addedNodes.push(param);
-        this._templateItem.appendChild(param);
-
-        if (i + 1 !== n) {
-          this._templateItem.appendChild(syntax(", "));
-        }
-      }
-
-      this._templateItem.appendChild(syntax(")"));
-    }
-
-    // Flatten the DOM by removing one redundant box (the template container).
-    for (let node of this._template.childNodes) {
-      fragment.appendChild(node.cloneNode(true));
-    }
-
-    // Remove any added nodes from the template.
-    for (let node of addedNodes) {
-      this._templateItem.removeChild(node);
-    }
-
-    return fragment;
-  }
-});
-
-DebuggerView.Tracer = new TracerView(DebuggerController, DebuggerView);
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -66,17 +66,16 @@ browser.jar:
     content/browser/devtools/codemirror/mozilla.css                    (sourceeditor/codemirror/mozilla.css)
     content/browser/devtools/debugger.xul                              (debugger/debugger.xul)
     content/browser/devtools/debugger.css                              (debugger/debugger.css)
     content/browser/devtools/debugger-controller.js                    (debugger/debugger-controller.js)
     content/browser/devtools/debugger-view.js                          (debugger/debugger-view.js)
     content/browser/devtools/debugger/workers-view.js                  (debugger/views/workers-view.js)
     content/browser/devtools/debugger/sources-view.js                  (debugger/views/sources-view.js)
     content/browser/devtools/debugger/variable-bubble-view.js          (debugger/views/variable-bubble-view.js)
-    content/browser/devtools/debugger/tracer-view.js                   (debugger/views/tracer-view.js)
     content/browser/devtools/debugger/watch-expressions-view.js        (debugger/views/watch-expressions-view.js)
     content/browser/devtools/debugger/event-listeners-view.js          (debugger/views/event-listeners-view.js)
     content/browser/devtools/debugger/global-search-view.js            (debugger/views/global-search-view.js)
     content/browser/devtools/debugger/toolbar-view.js                  (debugger/views/toolbar-view.js)
     content/browser/devtools/debugger/options-view.js                  (debugger/views/options-view.js)
     content/browser/devtools/debugger/stack-frames-view.js             (debugger/views/stack-frames-view.js)
     content/browser/devtools/debugger/stack-frames-classic-view.js     (debugger/views/stack-frames-classic-view.js)
     content/browser/devtools/debugger/filter-view.js                   (debugger/views/filter-view.js)
--- a/browser/devtools/markupview/test/browser_markupview_html_edit_02.js
+++ b/browser/devtools/markupview/test/browser_markupview_html_edit_02.js
@@ -2,16 +2,17 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test outerHTML edition via the markup-view
 
 loadHelperScript("helper_outerhtml_test_runner.js");
+requestLongerTimeout(2);
 
 const TEST_DATA = [
   {
     selector: "#badMarkup1",
     oldHTML: '<div id="badMarkup1">badMarkup1</div>',
     newHTML: '<div id="badMarkup1">badMarkup1</div> hanging</div>',
     validate: function*(pageNode, pageNodeFront, selectedNodeFront, inspector) {
       is(pageNodeFront, selectedNodeFront, "Original element is selected");
--- a/browser/devtools/performance/modules/logic/actors.js
+++ b/browser/devtools/performance/modules/logic/actors.js
@@ -14,16 +14,18 @@ loader.lazyRequireGetter(this, "Poller",
 loader.lazyRequireGetter(this, "CompatUtils",
   "devtools/performance/compatibility");
 loader.lazyRequireGetter(this, "RecordingUtils",
   "devtools/performance/recording-utils");
 loader.lazyRequireGetter(this, "TimelineFront",
   "devtools/server/actors/timeline", true);
 loader.lazyRequireGetter(this, "MemoryFront",
   "devtools/server/actors/memory", true);
+loader.lazyRequireGetter(this, "ProfilerFront",
+  "devtools/server/actors/profiler", true);
 
 // how often do we pull allocation sites from the memory actor
 const ALLOCATION_SITE_POLL_TIMER = 200; // ms
 
 // how often do we check the status of the profiler's circular buffer
 const PROFILER_CHECK_TIMER = 5000; // ms
 
 const MEMORY_ACTOR_METHODS = [
@@ -53,40 +55,40 @@ function ProfilerFrontFacade (target) {
 }
 
 ProfilerFrontFacade.prototype = {
   EVENTS: ["console-api-profiler", "profiler-stopped"],
 
   // Connects to the targets underlying real ProfilerFront.
   connect: Task.async(function*() {
     let target = this._target;
-    this._actor = yield CompatUtils.getProfiler(target);
+    this._front = new ProfilerFront(target.client, target.form);
 
     // Fetch and store information about the SPS profiler and
     // server profiler.
     this.traits = {};
     this.traits.filterable = target.getTrait("profilerDataFilterable");
 
     // Directly register to event notifications when connected
     // to hook into `console.profile|profileEnd` calls.
     yield this.registerEventNotifications({ events: this.EVENTS });
-    // TODO bug 1159389, listen directly to actor if supporting new front
-    target.client.addListener("eventNotification", this._onProfilerEvent);
+    this.EVENTS.forEach(e => this._front.on(e, this._onProfilerEvent));
   }),
 
   /**
    * Unregisters events for the underlying profiler actor.
    */
   destroy: Task.async(function *() {
     if (this._poller) {
       yield this._poller.destroy();
     }
+
+    this.EVENTS.forEach(e => this._front.off(e, this._onProfilerEvent));
     yield this.unregisterEventNotifications({ events: this.EVENTS });
-    // TODO bug 1159389, listen directly to actor if supporting new front
-    this._target.client.removeListener("eventNotification", this._onProfilerEvent);
+    yield this._front.destroy();
   }),
 
   /**
    * Starts the profiler actor, if necessary.
    *
    * @option {number?} bufferSize
    * @option {number?} sampleFrequency
    */
@@ -100,33 +102,40 @@ ProfilerFrontFacade.prototype = {
     if (!this._poller.isPolling()) {
       this._poller.on();
     }
 
     // Start the profiler only if it wasn't already active. The built-in
     // nsIPerformance module will be kept recording, because it's the same instance
     // for all targets and interacts with the whole platform, so we don't want
     // to affect other clients by stopping (or restarting) it.
-    let { isActive, currentTime, position, generation, totalSize } = yield this.getStatus();
+    let status = yield this.getStatus();
+
+    // This should only occur during teardown
+    if (!status) {
+      return;
+    }
+
+    let { isActive, currentTime, position, generation, totalSize } = status;
 
     if (isActive) {
       this.emit("profiler-already-active");
       return { startTime: currentTime, position, generation, totalSize };
     }
 
     // Translate options from the recording model into profiler-specific
     // options for the nsIProfiler
     let profilerOptions = {
       entries: options.bufferSize,
       interval: options.sampleFrequency ? (1000 / (options.sampleFrequency * 1000)) : void 0
     };
 
     let startInfo = yield this.startProfiler(profilerOptions);
     let startTime = 0;
-    if ('currentTime' in startInfo) {
+    if ("currentTime" in startInfo) {
       startTime = startInfo.currentTime;
     }
 
     this.emit("profiler-activated");
     return { startTime, position, generation, totalSize };
   }),
 
   /**
@@ -137,17 +146,17 @@ ProfilerFrontFacade.prototype = {
   stop: Task.async(function *() {
     yield this._poller.off();
   }),
 
   /**
    * Wrapper around `profiler.isActive()` to take profiler status data and emit.
    */
   getStatus: Task.async(function *() {
-    let data = yield (CompatUtils.actorCompatibilityBridge("isActive").call(this));
+    let data = yield CompatUtils.callFrontMethod("isActive").call(this);
     // If no data, the last poll for `isActive()` was wrapping up, and the target.client
     // is now null, so we no longer have data, so just abort here.
     if (!data) {
       return;
     }
 
     // If TEST_PROFILER_FILTER_STATUS defined (via array of fields), filter
     // out any field from isActive, used only in tests. Used to filter out
@@ -164,17 +173,17 @@ ProfilerFrontFacade.prototype = {
     this.emit("profiler-status", data);
     return data;
   }),
 
   /**
    * Returns profile data from now since `startTime`.
    */
   getProfile: Task.async(function *(options) {
-    let profilerData = yield (CompatUtils.actorCompatibilityBridge("getProfile").call(this, options));
+    let profilerData = yield CompatUtils.callFrontMethod("getProfile").call(this, options);
     // If the backend is not deduped, dedupe it ourselves, as rest of the code
     // expects a deduped profile.
     if (profilerData.profile.meta.version === 2) {
       RecordingUtils.deflateProfile(profilerData.profile);
     }
 
     // If the backend does not support filtering by start and endtime on platform (< Fx40),
     // do it on the client (much slower).
@@ -186,17 +195,19 @@ ProfilerFrontFacade.prototype = {
   }),
 
   /**
    * Invoked whenever a registered event was emitted by the profiler actor.
    *
    * @param object response
    *        The data received from the backend.
    */
-  _onProfilerEvent: function (_, { topic, subject, details }) {
+  _onProfilerEvent: function (data) {
+    let { subject, topic, details } = data;
+
     if (topic === "console-api-profiler") {
       if (subject.action === "profile") {
         this.emit("console-profile-start", details);
       } else if (subject.action === "profileEnd") {
         this.emit("console-profile-end", details);
       }
     } else if (topic === "profiler-stopped") {
       this.emit("profiler-stopped");
@@ -219,37 +230,37 @@ function TimelineFrontFacade (target) {
   EventEmitter.decorate(this);
 }
 
 TimelineFrontFacade.prototype = {
   EVENTS: ["markers", "frames", "memory", "ticks"],
 
   connect: Task.async(function*() {
     let supported = yield CompatUtils.timelineActorSupported(this._target);
-    this._actor = supported ?
+    this._front = supported ?
                   new TimelineFront(this._target.client, this._target.form) :
                   new CompatUtils.MockTimelineFront();
 
     this.IS_MOCK = !supported;
 
     // Binds underlying actor events and consolidates them to a `timeline-data`
     // exposed event.
     this.EVENTS.forEach(type => {
       let handler = this[`_on${type}`] = this._onTimelineData.bind(this, type);
-      this._actor.on(type, handler);
+      this._front.on(type, handler);
     });
   }),
 
   /**
    * Override actor's destroy, so we can unregister listeners before
    * destroying the underlying actor.
    */
   destroy: Task.async(function *() {
-    this.EVENTS.forEach(type => this._actor.off(type, this[`_on${type}`]));
-    yield this._actor.destroy();
+    this.EVENTS.forEach(type => this._front.off(type, this[`_on${type}`]));
+    yield this._front.destroy();
   }),
 
   /**
    * An aggregate of all events (markers, frames, memory, ticks) and exposes
    * to PerformanceFront as a single event.
    */
   _onTimelineData: function (type, ...data) {
     this.emit("timeline-data", type, ...data);
@@ -266,31 +277,31 @@ function MemoryFrontFacade (target) {
   this._pullAllocationSites = this._pullAllocationSites.bind(this);
 
   EventEmitter.decorate(this);
 }
 
 MemoryFrontFacade.prototype = {
   connect: Task.async(function*() {
     let supported = yield CompatUtils.memoryActorSupported(this._target);
-    this._actor = supported ?
+    this._front = supported ?
                   new MemoryFront(this._target.client, this._target.form) :
                   new CompatUtils.MockMemoryFront();
 
     this.IS_MOCK = !supported;
   }),
 
   /**
    * Disables polling and destroys actor.
    */
   destroy: Task.async(function *() {
     if (this._poller) {
       yield this._poller.destroy();
     }
-    yield this._actor.destroy();
+    yield this._front.destroy();
   }),
 
   /**
    * Starts polling for allocation information.
    */
   start: Task.async(function *(options) {
     if (!options.withAllocations) {
       return 0;
@@ -369,15 +380,15 @@ MemoryFrontFacade.prototype = {
 
     deferred.resolve();
   }),
 
   toString: () => "[object MemoryFrontFacade]"
 };
 
 // Bind all the methods that directly proxy to the actor
-PROFILER_ACTOR_METHODS.forEach(m => ProfilerFrontFacade.prototype[m] = CompatUtils.actorCompatibilityBridge(m));
-TIMELINE_ACTOR_METHODS.forEach(m => TimelineFrontFacade.prototype[m] = CompatUtils.actorCompatibilityBridge(m));
-MEMORY_ACTOR_METHODS.forEach(m => MemoryFrontFacade.prototype[m] = CompatUtils.actorCompatibilityBridge(m));
+PROFILER_ACTOR_METHODS.forEach(m => ProfilerFrontFacade.prototype[m] = CompatUtils.callFrontMethod(m));
+TIMELINE_ACTOR_METHODS.forEach(m => TimelineFrontFacade.prototype[m] = CompatUtils.callFrontMethod(m));
+MEMORY_ACTOR_METHODS.forEach(m => MemoryFrontFacade.prototype[m] = CompatUtils.callFrontMethod(m));
 
 exports.ProfilerFront = ProfilerFrontFacade;
 exports.TimelineFront = TimelineFrontFacade;
 exports.MemoryFront = MemoryFrontFacade;
--- a/browser/devtools/performance/modules/logic/compatibility.js
+++ b/browser/devtools/performance/modules/logic/compatibility.js
@@ -96,83 +96,29 @@ function timelineActorSupported(target) 
   if (target.TEST_MOCK_TIMELINE_ACTOR) {
     return false;
   }
 
   return target.hasActor("timeline");
 }
 
 /**
- * Returns a promise resolving to the location of the profiler actor
- * within this context.
- *
- * @param {TabTarget} target
- * @return {Promise<ProfilerActor>}
+ * Returns a function to be used as a method on an "Front" in ./actors.
+ * Calls the underlying actor's method.
  */
-function getProfiler (target) {
-  let deferred = promise.defer();
-  // Chrome and content process targets already have obtained a reference
-  // to the profiler tab actor. Use it immediately.
-  if (target.form && target.form.profilerActor) {
-    deferred.resolve(target.form.profilerActor);
-  }
-  // Check if we already have a grip to the `listTabs` response object
-  // and, if we do, use it to get to the profiler actor.
-  else if (target.root && target.root.profilerActor) {
-    deferred.resolve(target.root.profilerActor);
-  }
-  // Otherwise, call `listTabs`.
-  else {
-    target.client.listTabs(({ profilerActor }) => deferred.resolve(profilerActor));
-  }
-  return deferred.promise;
-}
-
-/**
- * Makes a request to an actor that does not have the modern `Front`
- * interface.
- */
-function legacyRequest (target, actor, method, args) {
-  let deferred = promise.defer();
-  let data = args[0] || {};
-  data.to = actor;
-  data.type = method;
-  target.client.request(data, deferred.resolve);
-  return deferred.promise;
-}
-
-/**
- * Returns a function to be used as a method on an "Actor" in ./actors.
- * Calls the underlying actor's method, supporting the modern `Front`
- * interface if possible, otherwise, falling back to using
- * `legacyRequest`.
- */
-function actorCompatibilityBridge (method) {
+function callFrontMethod (method) {
   return function () {
     // If there's no target or client on this actor facade,
     // abort silently -- this occurs in tests when polling occurs
     // after the test ends, when tests do not wait for toolbox destruction
     // (which will destroy the actor facade, turning off the polling).
     if (!this._target || !this._target.client) {
       return;
     }
-    // Check to see if this is a modern ActorFront, which has its
-    // own `request` method. Also, check if its a mock actor, as it mimicks
-    // the ActorFront interface.
-    // The profiler actor does not currently support the modern `Front`
-    // interface, so we have to manually push packets to it.
-    // TODO bug 1159389, fix up profiler actor to not need this, however
-    // we will need it for backwards compat
-    if (this.IS_MOCK || this._actor.request) {
-      return this._actor[method].apply(this._actor, arguments);
-    }
-    else {
-      return legacyRequest(this._target, this._actor, method, arguments);
-    }
+    return this._front[method].apply(this._front, arguments);
   };
 }
 
 exports.MockMemoryFront = MockMemoryFront;
 exports.MockTimelineFront = MockTimelineFront;
 exports.memoryActorSupported = memoryActorSupported;
 exports.timelineActorSupported = timelineActorSupported;
-exports.getProfiler = getProfiler;
-exports.actorCompatibilityBridge = actorCompatibilityBridge;
+exports.callFrontMethod = callFrontMethod;
--- a/browser/devtools/performance/modules/logic/frame-utils.js
+++ b/browser/devtools/performance/modules/logic/frame-utils.js
@@ -33,16 +33,19 @@ const CHAR_CODE_COLON = ":".charCodeAt(0
 const CHAR_CODE_SLASH = "/".charCodeAt(0);
 
 // The cache used in the `nsIURL` function.
 const gNSURLStore = new Map();
 
 // The cache used to store inflated frames.
 const gInflatedFrameStore = new WeakMap();
 
+// The cache used to store frame data from `getInfo`.
+const gFrameData = new WeakMap();
+
 /**
  * Parses the raw location of this function call to retrieve the actual
  * function name, source url, host name, line and column.
  */
 function parseLocation(location, fallbackLine, fallbackColumn) {
   // Parse the `location` for the function name, source url, line, column etc.
 
   let line, column, url;
@@ -444,13 +447,76 @@ function isChromeScheme(location, i) {
     return false;
   }
 }
 
 function isNumeric(c) {
   return c >= CHAR_CODE_0 && c <= CHAR_CODE_9;
 }
 
+/**
+ * Calculates the relative costs of this frame compared to a root,
+ * and generates allocations information if specified. Uses caching
+ * if possible.
+ *
+ * @param {ThreadNode|FrameNode} node
+ *                               The node we are calculating.
+ * @param {ThreadNode} options.root
+ *                     The root thread node to calculate relative costs.
+ *                     Generates [self|total] [duration|percentage] values.
+ * @param {boolean} options.allocations
+ *                  Generates `totalAllocations` and `selfAllocations`.
+ *
+ * @return {object}
+ */
+function getFrameInfo (node, options) {
+  let data = gFrameData.get(node);
+
+  if (!data) {
+    if (node.nodeType === "Thread") {
+      data = Object.create(null);
+      data.functionName = global.L10N.getStr("table.root");
+    } else {
+      data = parseLocation(node.location, node.line, node.column);
+      data.hasOptimizations = node.hasOptimizations();
+      data.isContent = node.isContent;
+      data.isMetaCategory = node.isMetaCategory;
+    }
+    data.samples = node.youngestFrameSamples;
+    data.categoryData = global.CATEGORY_MAPPINGS[node.category] || {};
+    data.nodeType = node.nodeType;
+
+    // Frame name (function location or some meta information)
+    data.name = data.isMetaCategory ? data.categoryData.label : data.functionName || "";
+    data.tooltiptext = data.isMetaCategory ? data.categoryData.label : node.location || "";
+
+    gFrameData.set(node, data);
+  }
+
+  // If a root specified, calculate the relative costs in the context of
+  // this call tree. The cached store may already have this, but generate
+  // if it does not.
+  if (options && options.root && !data.COSTS_CALCULATED) {
+    let totalSamples = options.root.samples;
+    let totalDuration = options.root.duration;
+
+    data.selfDuration = node.youngestFrameSamples / totalSamples * totalDuration;
+    data.selfPercentage = node.youngestFrameSamples / totalSamples * 100;
+    data.totalDuration = node.samples / totalSamples * totalDuration;
+    data.totalPercentage = node.samples / totalSamples * 100;
+    data.COSTS_CALCULATED = true;
+  }
+
+  if (options && options.allocations && !data.ALLOCATIONS_CALCULATED) {
+    data.totalAllocations = node.allocations + node.calls.reduce((acc, node) => acc + node.allocations, 0);
+    data.selfAllocations = node.allocations;
+    data.ALLOCATIONS_CALCULATED = true;
+  }
+
+  return data;
+}
+
+exports.getFrameInfo = getFrameInfo;
 exports.computeIsContentAndCategory = computeIsContentAndCategory;
 exports.parseLocation = parseLocation;
 exports.getInflatedFrameCache = getInflatedFrameCache;
 exports.getOrAddInflatedFrame = getOrAddInflatedFrame;
 exports.InflatedFrame = InflatedFrame;
--- a/browser/devtools/performance/modules/logic/front.js
+++ b/browser/devtools/performance/modules/logic/front.js
@@ -12,16 +12,18 @@ loader.lazyRequireGetter(this, "EventEmi
   "devtools/toolkit/event-emitter");
 loader.lazyRequireGetter(this, "extend",
   "sdk/util/object", true);
 
 loader.lazyRequireGetter(this, "Actors",
   "devtools/performance/actors");
 loader.lazyRequireGetter(this, "RecordingModel",
   "devtools/performance/recording-model", true);
+loader.lazyRequireGetter(this, "normalizePerformanceFeatures",
+  "devtools/performance/recording-utils", true);
 loader.lazyRequireGetter(this, "DevToolsUtils",
   "devtools/toolkit/DevToolsUtils");
 
 loader.lazyImporter(this, "gDevTools",
   "resource:///modules/devtools/gDevTools.jsm");
 
 /**
  * A cache of all PerformanceFront instances.
@@ -311,26 +313,31 @@ PerformanceFront.prototype = {
    *
    * @param object options
    *        An options object to pass to the actors. Supported properties are
    *        `withTicks`, `withMemory` and `withAllocations`, `probability`, and `maxLogLength`.
    * @return object
    *         A promise that is resolved once recording has started.
    */
   startRecording: Task.async(function*(options = {}) {
-    let model = new RecordingModel(options);
+    let model = new RecordingModel(normalizePerformanceFeatures(options, this.getActorSupport()));
+
     this.emit("recording-starting", model);
 
     // All actors are started asynchronously over the remote debugging protocol.
     // Get the corresponding start times from each one of them.
     // The timeline and memory actors are target-dependent, so start those as well,
     // even though these are mocked in older Geckos (FF < 35)
-    let { startTime, position, generation, totalSize } = yield this._profiler.start(options);
-    let timelineStartTime = yield this._timeline.start(options);
-    let memoryStartTime = yield this._memory.start(options);
+    let profilerStart = this._profiler.start(options);
+    let timelineStart = this._timeline.start(options);
+    let memoryStart = this._memory.start(options);
+
+    let { startTime, position, generation, totalSize } = yield profilerStart;
+    let timelineStartTime = yield timelineStart;
+    let memoryStartTime = yield memoryStart;
 
     let data = {
       profilerStartTime: startTime, timelineStartTime, memoryStartTime,
       generation, position, totalSize
     };
 
     // Signify to the model that the recording has started,
     // populate with data and store the recording model here.
--- a/browser/devtools/performance/modules/logic/marker-utils.js
+++ b/browser/devtools/performance/modules/logic/marker-utils.js
@@ -22,16 +22,20 @@ loader.lazyRequireGetter(this, "WebConso
 // String used to fill in platform data when it should be hidden.
 const GECKO_SYMBOL = "(Gecko)";
 
 /**
  * Takes a marker, blueprint, and filter list and
  * determines if this marker should be filtered or not.
  */
 function isMarkerValid (marker, filter) {
+  if (!filter || filter.length === 0) {
+    return true;
+  }
+
   let isUnknown = !(marker.name in TIMELINE_BLUEPRINT);
   if (isUnknown) {
     return filter.indexOf("UNKNOWN") === -1;
   }
   return filter.indexOf(marker.name) === -1;
 }
 
 /**
@@ -288,90 +292,16 @@ const DOM = {
       }
     }
 
     return container;
   }
 };
 
 /**
- * A series of collapsers used by the blueprint. These functions are
- * invoked on a moving window of two markers.
- *
- * A function determining how markers are collapsed together.
- * Invoked with 3 arguments: the current parent marker, the
- * current marker and a method for peeking i markers ahead. If
- * nothing is returned, the marker is added as a standalone entry
- * in the waterfall. Otherwise, an object needs to be returned
- * with the following properties:
- * - toParent: The marker to be made a new parent. Can use the current
- *             marker, becoming a parent itself, or make a new marker-esque
- *             object.
- * - collapse: Whether or not this current marker should be nested within
- *             the current parent.
- * - finalize: Whether or not the current parent should be finalized and popped
- *        off the stack.
- */
-const CollapseFunctions = {
-  /**
-   * Combines similar markers that are consecutive into a meta marker.
-   */
-  identical: function (parent, curr, peek) {
-    let next = peek(1);
-    // If there is a parent marker currently being filled and the current marker
-    // should go into the parent marker, make it so.
-    if (parent && parent.name == curr.name) {
-      let finalize = next && next.name !== curr.name;
-      return { collapse: true, finalize };
-    }
-    // Otherwise if the current marker is the same type as the next marker type,
-    // create a new parent marker containing the current marker.
-    if (next && curr.name == next.name) {
-      return { toParent: { name: curr.name, start: curr.start }, collapse: true };
-    }
-  },
-
-  /**
-   * Combines similar markers that are close to each other in time into a meta marker.
-   */
-  adjacent: function (parent, curr, peek) {
-    let next = peek(1);
-    if (next && (next.start < curr.end || next.start - curr.end <= 10 /* ms */)) {
-      return CollapseFunctions.identical(parent, curr, peek);
-    }
-  },
-
-  /**
-   * Folds this marker in parent marker if parent marker fully eclipses
-   * the current markers' time.
-   */
-  child: function (parent, curr, peek) {
-    let next = peek(1);
-    // If this marker is consumed by current parent, collapse
-    if (parent && curr.end <= parent.end) {
-      let finalize = next && next.end > parent.end;
-      return { collapse: true, finalize };
-    }
-  },
-
-  /**
-   * Turns this marker into a parent marker if the next marker
-   * is fully eclipsed by the current marker.
-   */
-  parent: function (parent, curr, peek) {
-    let next = peek(1);
-    // If the next marker is fully consumed by this marker, make
-    // it a parent (do not collapse, the marker becomes a parent).
-    if (next && curr.end >= next.end) {
-      return { toParent: curr };
-    }
-  },
-};
-
-/**
  * Mapping of JS marker causes to a friendlier form. Only
  * markers that are considered "from content" should be labeled here.
  */
 const JS_MARKER_MAP = {
   "<script> element":          "Script Tag",
   "setInterval handler":       "setInterval",
   "setTimeout handler":        "setTimeout",
   "FrameRequestCallback":      "requestAnimationFrame",
@@ -475,11 +405,10 @@ function getBlueprintFor (marker) {
   return TIMELINE_BLUEPRINT[marker.name] || TIMELINE_BLUEPRINT.UNKNOWN;
 }
 
 exports.isMarkerValid = isMarkerValid;
 exports.getMarkerLabel = getMarkerLabel;
 exports.getMarkerClassName = getMarkerClassName;
 exports.getMarkerFields = getMarkerFields;
 exports.DOM = DOM;
-exports.CollapseFunctions = CollapseFunctions;
 exports.Formatters = Formatters;
 exports.getBlueprintFor = getBlueprintFor;
--- a/browser/devtools/performance/modules/logic/recording-utils.js
+++ b/browser/devtools/performance/modules/logic/recording-utils.js
@@ -1,21 +1,54 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
+loader.lazyRequireGetter(this, "extend",
+  "sdk/util/object", true);
 
 /**
  * Utility functions for managing recording models and their internal data,
  * such as filtering profile samples or offsetting timestamps.
  */
 
 /**
+ * Takes an options object for `startRecording`, and normalizes
+ * it based off of server support. For example, if the user
+ * requests to record memory `withMemory = true`, but the server does
+ * not support that feature, then the `false` will overwrite user preference
+ * in order to define the recording with what is actually available, not
+ * what the user initially requested.
+ *
+ * @param {object} options
+ * @param {boolean} support.timeline
+ * @param {boolean} support.memory
+ * @param {boolean}
+ */
+function normalizePerformanceFeatures (options, support) {
+  let supportOptions = Object.create(null);
+
+  // TODO bug 1172180 disable `withAllocations` and `withJITOptimizations` when using the
+  // pseudo front, as we only want to support it directly from the real actor
+  // in Fx42+
+  if (!support.memory) {
+    supportOptions.withMemory = false;
+    supportOptions.withAllocations = false;
+  }
+  if (!support.timeline) {
+    supportOptions.withMarkers = false;
+    supportOptions.withTicks = false;
+  }
+
+  return extend(options, supportOptions);
+}
+
+/**
  * Filters all the samples in the provided profiler data to be more recent
  * than the specified start time.
  *
  * @param object profile
  *        The profiler data received from the backend.
  * @param number profilerStartTime
  *        The earliest acceptable sample time (in milliseconds).
  */
@@ -526,16 +559,17 @@ UniqueStacks.prototype.getOrAddStackInde
   stackTable.push([prefixIndex, frameIndex]);
   return index;
 };
 
 UniqueStacks.prototype.getOrAddStringIndex = function(s) {
   return this._uniqueStrings.getOrAddStringIndex(s);
 };
 
+exports.normalizePerformanceFeatures = normalizePerformanceFeatures;
 exports.filterSamples = filterSamples;
 exports.offsetSampleTimes = offsetSampleTimes;
 exports.offsetMarkerTimes = offsetMarkerTimes;
 exports.offsetAndScaleTimestamps = offsetAndScaleTimestamps;
 exports.getProfileThreadFromAllocations = getProfileThreadFromAllocations;
 exports.deflateProfile = deflateProfile;
 exports.deflateThread = deflateThread;
 exports.UniqueStrings = UniqueStrings;
--- a/browser/devtools/performance/modules/logic/tree-model.js
+++ b/browser/devtools/performance/modules/logic/tree-model.js
@@ -1,19 +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/. */
 "use strict";
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
 
-loader.lazyRequireGetter(this, "L10N",
-  "devtools/performance/global", true);
-loader.lazyRequireGetter(this, "CATEGORY_MAPPINGS",
-  "devtools/performance/global", true);
 loader.lazyRequireGetter(this, "JITOptimizations",
   "devtools/performance/jit", true);
 loader.lazyRequireGetter(this, "FrameUtils",
   "devtools/performance/frame-utils");
 
 /**
  * A call tree for a thread. This is essentially a linkage between all frames
  * of all samples into a single tree structure, with additional information
@@ -34,16 +30,17 @@ function ThreadNode(thread, options = {}
   if (options.endTime == void 0 || options.startTime == void 0) {
     throw new Error("ThreadNode requires both `startTime` and `endTime`.");
   }
   this.samples = 0;
   this.sampleTimes = [];
   this.youngestFrameSamples = 0;
   this.calls = [];
   this.duration = options.endTime - options.startTime;
+  this.nodeType = "Thread";
 
   let { samples, stackTable, frameTable, stringTable, allocationsTable } = thread;
 
   // Nothing to do if there are no samples.
   if (samples.data.length === 0) {
     return;
   }
 
@@ -214,31 +211,36 @@ ThreadNode.prototype = {
 
         // An empty frame key means this frame should be skipped.
         if (frameKey === "") {
           continue;
         }
 
         // If we shouldn't flatten the current frame into the previous one, advance a
         // level in the call tree.
-        if (!flattenRecursion || frameKey !== prevFrameKey) {
+        let shouldFlatten = flattenRecursion && frameKey === prevFrameKey;
+        if (!shouldFlatten) {
           calls = prevCalls;
         }
 
         let frameNode = getOrAddFrameNode(calls, isLeaf, frameKey, inflatedFrame,
                                           mutableFrameKeyOptions.isMetaCategoryOut,
                                           leafTable);
         if (isLeaf) {
           frameNode.youngestFrameSamples++;
           if (inflatedFrame.optimizations) {
             frameNode._addOptimizations(inflatedFrame.optimizations, inflatedFrame.implementation,
                                         sampleTime, stringTable);
           }
         }
-        frameNode.samples++;
+
+        // Don't overcount flattened recursive frames.
+        if (!shouldFlatten) {
+          frameNode.samples++;
+        }
 
         prevFrameKey = frameKey;
         prevCalls = frameNode.calls;
         isLeaf = mutableFrameKeyOptions.isLeaf = false;
       }
 
       this.samples++;
       this.sampleTimes.push(sampleTime);
@@ -297,24 +299,22 @@ ThreadNode.prototype = {
 
     // Replace the toplevel calls with rootCalls, which now contains the
     // uninverted roots.
     this.calls = rootCalls;
   },
 
   /**
    * Gets additional details about this node.
+   * @see FrameNode.prototype.getInfo for more information.
+   *
    * @return object
    */
-  getInfo: function() {
-    return {
-      nodeType: "Thread",
-      functionName: L10N.getStr("table.root"),
-      categoryData: {}
-    };
+  getInfo: function(options) {
+    return FrameUtils.getFrameInfo(this, options);
   },
 
   /**
    * Mimicks the interface of FrameNode, and a ThreadNode can never have
    * optimization data (at the moment, anyway), so provide a function
    * to return null so we don't need to check if a frame node is a thread
    * or not everytime we fetch optimization data.
    *
@@ -368,16 +368,17 @@ function FrameNode(frameKey, { location,
   this.samples = 0;
   this.calls = [];
   this.isContent = !!isContent;
   this._optimizations = null;
   this._tierData = null;
   this._stringTable = null;
   this.isMetaCategory = !!isMetaCategory;
   this.category = category;
+  this.nodeType = "Frame";
 }
 
 FrameNode.prototype = {
   /**
    * Take optimization data observed for this frame.
    *
    * @param object optimizationSite
    *               Any JIT optimization information attached to the current
@@ -433,40 +434,31 @@ FrameNode.prototype = {
       for (let i = 0; i < otherOpts.length; i++) {
         opts.push(otherOpts[i]);
       }
     }
   },
 
   /**
    * Returns the parsed location and additional data describing
-   * this frame. Uses cached data if possible.
+   * this frame. Uses cached data if possible. Takes the following
+   * options:
+   *
+   * @param {ThreadNode} options.root
+   *                     The root thread node to calculate relative costs.
+   *                     Generates [self|total] [duration|percentage] values.
+   * @param {boolean} options.allocations
+   *                  Generates `totalAllocations` and `selfAllocations`.
    *
    * @return object
    *         The computed { name, file, url, line } properties for this
-   *         function call.
-   */
-  getInfo: function() {
-    return this._data || this._computeInfo();
-  },
-
-  /**
-   * Parses the raw location of this function call to retrieve the actual
-   * function name and source url.
+   *         function call, as well as additional params if options specified.
    */
-  _computeInfo: function() {
-    let categoryData = CATEGORY_MAPPINGS[this.category] || {};
-    let parsedData = FrameUtils.parseLocation(this.location, this.line, this.column);
-    parsedData.nodeType = "Frame";
-    parsedData.categoryData = categoryData;
-    parsedData.isContent = this.isContent;
-    parsedData.isMetaCategory = this.isMetaCategory;
-    parsedData.hasOptimizations = this.hasOptimizations();
-
-    return this._data = parsedData;
+  getInfo: function(options) {
+    return FrameUtils.getFrameInfo(this, options);
   },
 
   /**
    * Returns whether or not the frame node has an JITOptimizations model.
    *
    * @return {Boolean}
    */
   hasOptimizations: function () {
--- a/browser/devtools/performance/modules/logic/waterfall-utils.js
+++ b/browser/devtools/performance/modules/logic/waterfall-utils.js
@@ -2,83 +2,102 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 /**
  * Utility functions for collapsing markers into a waterfall.
  */
 
+loader.lazyRequireGetter(this, "extend",
+  "sdk/util/object", true);
 loader.lazyRequireGetter(this, "MarkerUtils",
   "devtools/performance/marker-utils");
 
 /**
+ * Creates a parent marker, which functions like a regular marker,
+ * but is able to hold additional child markers.
+ *
+ * The marker is seeded with values from `marker`.
+ * @param object marker
+ * @return object
+ */
+function createParentNode (marker) {
+  return extend(marker, { submarkers: [] });
+}
+
+
+/**
  * Collapses markers into a tree-like structure.
- * @param object markerNode
+ * @param object rootNode
  * @param array markersList
  * @param array filter
  */
-function collapseMarkersIntoNode({ markerNode, markersList, filter }) {
-  let { getCurrentParentNode, collapseMarker, addParentNode, popParentNode } = createParentNodeFactory(markerNode);
+function collapseMarkersIntoNode({ rootNode, markersList, filter }) {
+  let { getCurrentParentNode, pushNode, popParentNode } = createParentNodeFactory(rootNode);
 
   for (let i = 0, len = markersList.length; i < len; i++) {
     let curr = markersList[i];
 
     // If this marker type should not be displayed, just skip
     if (!MarkerUtils.isMarkerValid(curr, filter)) {
       continue;
     }
 
     let parentNode = getCurrentParentNode();
     let blueprint = MarkerUtils.getBlueprintFor(curr);
-    let collapse = blueprint.collapseFunc || (() => null);
-    let peek = distance => markersList[i + distance];
+
+    let nestable = "nestable" in blueprint ? blueprint.nestable : true;
+    let collapsible = "collapsible" in blueprint ? blueprint.collapsible : true;
+
+    let finalized = null;
 
-    let collapseInfo = collapse(parentNode, curr, peek);
-    if (collapseInfo) {
-      let { collapse, toParent, finalize } = collapseInfo;
+    // If this marker is collapsible, turn it into a parent marker.
+    // If there are no children within it later, it will be turned
+    // back into a normal node.
+    if (collapsible) {
+      curr = createParentNode(curr);
+    }
 
-      // If `toParent` is an object, use it as the next parent marker
-      if (typeof toParent === "object") {
-        addParentNode(toParent);
-      }
+    // If not nestible, just push it inside the root node,
+    // like console.time/timeEnd.
+    if (!nestable) {
+      pushNode(rootNode, curr);
+      continue;
+    }
 
-      if (collapse) {
-        collapseMarker(curr);
+    // First off, if any parent nodes exist, finish them off
+    // recursively upwards if this marker is outside their ranges and nestable.
+    while (!finalized && parentNode) {
+      // If this marker is eclipsed by the current parent marker,
+      // make it a child of the current parent and stop
+      // going upwards.
+      if (nestable && curr.end <= parentNode.end) {
+        pushNode(parentNode, curr);
+        finalized = true;
+        break;
       }
 
-      // If the marker specifies this parent marker is full,
-      // pop it from the stack.
-      if (finalize) {
+      // If this marker is still nestable, but outside of the range
+      // of the current parent, iterate upwards on the next parent
+      // and finalize the current parent.
+      if (nestable) {
         popParentNode();
+        parentNode = getCurrentParentNode();
+        continue;
       }
-    } else {
-      markerNode.submarkers.push(curr);
+    }
+
+    if (!finalized) {
+      pushNode(rootNode, curr);
     }
   }
 }
 
 /**
- * Creates a parent marker, which functions like a regular marker,
- * but is able to hold additional child markers.
- *
- * The marker is seeded with values from `marker`.
- * @param object marker
- * @return object
- */
-function makeParentMarkerNode (marker) {
-  let node = Object.create(null);
-  for (let prop in marker) {
-    node[prop] = marker[prop];
-  }
-  node.submarkers = [];
-  return node;
-}
-
-/**
  * Takes a root marker node and creates a hash of functions used
  * to manage the creation and nesting of additional parent markers.
  *
  * @param {object} root
  * @return {object}
  */
 function createParentNodeFactory (root) {
   let parentMarkers = [];
@@ -93,42 +112,43 @@ function createParentNodeFactory (root) 
       }
 
       let lastParent = parentMarkers.pop();
       // If this finished parent marker doesn't have an end time,
       // so probably a synthesized marker, use the last marker's end time.
       if (lastParent.end == void 0) {
         lastParent.end = lastParent.submarkers[lastParent.submarkers.length - 1].end;
       }
+
+      // If no children were ever pushed into this parent node,
+      // remove it's submarkers so it behaves like a non collapsible
+      // node.
+      if (!lastParent.submarkers.length) {
+        delete lastParent.submarkers;
+      }
+
       return lastParent;
     },
 
     /**
      * Returns the most recent parent node.
      */
     getCurrentParentNode: () => parentMarkers.length ? parentMarkers[parentMarkers.length - 1] : null,
 
     /**
-     * Push a new parent node onto the stack and nest it with the
-     * next most recent parent node, or root if no other parent nodes.
-     */
-    addParentNode: (marker) => {
-      let parentMarker = makeParentMarkerNode(marker);
-      (factory.getCurrentParentNode() || root).submarkers.push(parentMarker);
-      parentMarkers.push(parentMarker);
-    },
-
-    /**
      * Push this marker into the most recent parent node.
      */
-    collapseMarker: (marker) => {
-      if (parentMarkers.length === 0) {
-        throw new Error("Cannot collapse marker with no parents.");
+    pushNode: (parent, marker) => {
+      parent.submarkers.push(marker);
+
+      // If pushing a parent marker, track it as the top of
+      // the parent stack.
+      if (marker.submarkers) {
+        parentMarkers.push(marker);
       }
-      factory.getCurrentParentNode().submarkers.push(marker);
     }
   };
 
   return factory;
 }
 
-exports.makeParentMarkerNode = makeParentMarkerNode;
+exports.createParentNode = createParentNode;
 exports.collapseMarkersIntoNode = collapseMarkersIntoNode;
--- a/browser/devtools/performance/modules/markers.js
+++ b/browser/devtools/performance/modules/markers.js
@@ -1,15 +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/. */
 "use strict";
 
 const { L10N } = require("devtools/performance/global");
-const { Formatters, CollapseFunctions: collapse } = require("devtools/performance/marker-utils");
+const { Formatters } = require("devtools/performance/marker-utils");
 
 /**
  * A simple schema for mapping markers to the timeline UI. The keys correspond
  * to marker names, while the values are objects with the following format:
  *
  * - group: The row index in the timeline overview graph; multiple markers
  *          can be added on the same row. @see <overview.js/buildGraphImage>
  * - label: The label used in the waterfall to identify the marker. Can be a
@@ -18,29 +18,21 @@ const { Formatters, CollapseFunctions: c
  *          If you use a function for a label, it *must* handle the case where
  *          no marker is provided for a main label to describe all markers of
  *          this type.
  * - colorName: The label of the DevTools color used for this marker. If
  *              adding a new color, be sure to check that there's an entry
  *              for `.marker-details-bullet.{COLORNAME}` for the equivilent
  *              entry in ./browser/themes/shared/devtools/performance.inc.css
  *              https://developer.mozilla.org/en-US/docs/Tools/DevToolsColors
- * - collapseFunc: A function determining how markers are collapsed together.
- *                 Invoked with 3 arguments: the current parent marker, the
- *                 current marker and a method for peeking i markers ahead. If
- *                 nothing is returned, the marker is added as a standalone entry
- *                 in the waterfall. Otherwise, an object needs to be returned
- *                 with the following properties:
- *                 - toParent: The marker to be made a new parent. Can use the current
- *                             marker, becoming a parent itself, or make a new marker-esque
- *                             object.
- *                 - collapse: Whether or not this current marker should be nested within
- *                             the current parent.
- *                 - finalize: Whether or not the current parent should be finalized and popped
- *                             off the stack.
+ * - collapsible: Whether or not this marker can contain other markers it
+ *                eclipses, and becomes collapsible to reveal its nestable children.
+ *                Defaults to true.
+ * - nestable: Whether or not this marker can be nested inside an eclipsing
+ *             collapsible marker. Defaults to true.
  * - fields: An optional array of marker properties you wish to display in the
  *           marker details view. For example, a field in the array such as
  *           { property: "aCauseName", label: "Cause" } would render a string
  *           like `Cause: ${marker.aCauseName}` in the marker details view.
  *           Each `field` item may take the following properties:
  *           - property: The property that must exist on the marker to render,
  *                       and the value of the property will be displayed.
  *           - label: The name of the property that should be displayed.
@@ -56,131 +48,107 @@ const { Formatters, CollapseFunctions: c
  */
 const TIMELINE_BLUEPRINT = {
   /* Default definition used for markers that occur but
    * are not defined here. Should ultimately be defined, but this gives
    * us room to work on the front end separately from the platform. */
   "UNKNOWN": {
     group: 2,
     colorName: "graphs-grey",
-    collapseFunc: collapse.child,
-    label: Formatters.UnknownLabel
+    label: Formatters.UnknownLabel,
   },
 
   /* Group 0 - Reflow and Rendering pipeline */
   "Styles": {
     group: 0,
     colorName: "graphs-purple",
-    collapseFunc: collapse.child,
     label: L10N.getStr("timeline.label.styles2"),
     fields: Formatters.StylesFields,
   },
   "Reflow": {
     group: 0,
     colorName: "graphs-purple",
-    collapseFunc: collapse.child,
     label: L10N.getStr("timeline.label.reflow2"),
   },
   "Paint": {
     group: 0,
     colorName: "graphs-green",
-    collapseFunc: collapse.child,
     label: L10N.getStr("timeline.label.paint"),
   },
 
   /* Group 1 - JS */
   "DOMEvent": {
     group: 1,
     colorName: "graphs-yellow",
-    collapseFunc: collapse.parent,
     label: L10N.getStr("timeline.label.domevent"),
     fields: Formatters.DOMEventFields,
   },
   "Javascript": {
     group: 1,
     colorName: "graphs-yellow",
-    collapseFunc: either(collapse.parent, collapse.child),
     label: Formatters.JSLabel,
     fields: Formatters.JSFields
   },
   "Parse HTML": {
     group: 1,
     colorName: "graphs-yellow",
-    collapseFunc: either(collapse.parent, collapse.child),
     label: L10N.getStr("timeline.label.parseHTML"),
   },
   "Parse XML": {
     group: 1,
     colorName: "graphs-yellow",
-    collapseFunc: either(collapse.parent, collapse.child),
     label: L10N.getStr("timeline.label.parseXML"),
   },
   "GarbageCollection": {
     group: 1,
     colorName: "graphs-red",
-    collapseFunc: either(collapse.parent, collapse.child),
     label: Formatters.GCLabel,
     fields: [
       { property: "causeName", label: "Reason:" },
       { property: "nonincrementalReason", label: "Non-incremental Reason:" }
     ],
   },
   "nsCycleCollector::Collect": {
     group: 1,
     colorName: "graphs-red",
-    collapseFunc: either(collapse.parent, collapse.child),
     label: "Cycle Collection",
     fields: Formatters.CycleCollectionFields,
   },
   "nsCycleCollector::ForgetSkippable": {
     group: 1,
     colorName: "graphs-red",
-    collapseFunc: either(collapse.parent, collapse.child),
     label: "Cycle Collection",
     fields: Formatters.CycleCollectionFields,
   },
 
   /* Group 2 - User Controlled */
   "ConsoleTime": {
     group: 2,
     colorName: "graphs-blue",
     label: sublabelForProperty(L10N.getStr("timeline.label.consoleTime"), "causeName"),
     fields: [{
       property: "causeName",
       label: L10N.getStr("timeline.markerDetail.consoleTimerName")
     }],
+    nestable: false,
+    collapsible: false,
   },
   "TimeStamp": {
     group: 2,
     colorName: "graphs-blue",
-    collapseFunc: collapse.child,
     label: sublabelForProperty(L10N.getStr("timeline.label.timestamp"), "causeName"),
     fields: [{
       property: "causeName",
       label: "Label:"
     }],
+    collapsible: false,
   },
 };
 
 /**
- * Helper for creating a function that returns the first defined result from
- * a list of functions passed in as params, in order.
- * @param ...function fun
- * @return any
- */
-function either(...fun) {
-  return function() {
-    for (let f of fun) {
-      let result = f.apply(null, arguments);
-      if (result !== undefined) return result;
-    }
-  }
-}
-
-/**
  * Takes a main label (like "Timestamp") and a property,
  * and returns a marker that will print out the property
  * value for a marker if it exists ("Timestamp (rendering)"),
  * or just the main label if it does not.
  */
 function sublabelForProperty (mainLabel, prop) {
   return (marker={}) => marker[prop] ? `${mainLabel} (${marker[prop]})` : mainLabel;
 }
--- a/browser/devtools/performance/modules/widgets/tree-view.js
+++ b/browser/devtools/performance/modules/widgets/tree-view.js
@@ -128,49 +128,48 @@ function CallView({
 CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
   /**
    * Creates the view for this tree node.
    * @param nsIDOMNode document
    * @param nsIDOMNode arrowNode
    * @return nsIDOMNode
    */
   _displaySelf: function(document, arrowNode) {
-    let displayedData = this.getDisplayedData();
-    let frameInfo = this.frame.getInfo();
+    let frameInfo = this.getDisplayedData();
 
     if (this.visibleCells.duration) {
-      var durationCell = this._createTimeCell(document, displayedData.totalDuration);
+      var durationCell = this._createTimeCell(document, frameInfo.totalDuration);
     }
     if (this.visibleCells.selfDuration) {
-      var selfDurationCell = this._createTimeCell(document, displayedData.selfDuration, true);
+      var selfDurationCell = this._createTimeCell(document, frameInfo.selfDuration, true);
     }
     if (this.visibleCells.percentage) {
-      var percentageCell = this._createExecutionCell(document, displayedData.totalPercentage);
+      var percentageCell = this._createExecutionCell(document, frameInfo.totalPercentage);
     }
     if (this.visibleCells.selfPercentage) {
-      var selfPercentageCell = this._createExecutionCell(document, displayedData.selfPercentage, true);
+      var selfPercentageCell = this._createExecutionCell(document, frameInfo.selfPercentage, true);
     }
     if (this.visibleCells.allocations) {
-      var allocationsCell = this._createAllocationsCell(document, displayedData.totalAllocations);
+      var allocationsCell = this._createAllocationsCell(document, frameInfo.totalAllocations);
     }
     if (this.visibleCells.selfAllocations) {
-      var selfAllocationsCell = this._createAllocationsCell(document, displayedData.selfAllocations, true);
+      var selfAllocationsCell = this._createAllocationsCell(document, frameInfo.selfAllocations, true);
     }
     if (this.visibleCells.samples) {
-      var samplesCell = this._createSamplesCell(document, displayedData.samples);
+      var samplesCell = this._createSamplesCell(document, frameInfo.samples);
     }
     if (this.visibleCells.function) {
-      var functionCell = this._createFunctionCell(document, arrowNode, displayedData.name, frameInfo, this.level);
+      var functionCell = this._createFunctionCell(document, arrowNode, frameInfo.name, frameInfo, this.level);
     }
 
     let targetNode = document.createElement("hbox");
     targetNode.className = "call-tree-item";
     targetNode.setAttribute("origin", frameInfo.isContent ? "content" : "chrome");
     targetNode.setAttribute("category", frameInfo.categoryData.abbrev || "");
-    targetNode.setAttribute("tooltiptext", displayedData.tooltiptext);
+    targetNode.setAttribute("tooltiptext", frameInfo.tooltiptext);
 
     if (this.hidden) {
       targetNode.style.display = "none";
     }
     if (this.visibleCells.duration) {
       targetNode.appendChild(durationCell);
     }
     if (this.visibleCells.percentage) {
@@ -350,80 +349,36 @@ CallView.prototype = Heritage.extend(Abs
    *
    * @return object
    */
   getDisplayedData: function() {
     if (this._cachedDisplayedData) {
       return this._cachedDisplayedData;
     }
 
-    let data = this._cachedDisplayedData = Object.create(null);
-    let frameInfo = this.frame.getInfo();
+    return this._cachedDisplayedData = this.frame.getInfo({
+      root: this.root.frame,
+      allocations: (this.visibleCells.allocations || this.visibleCells.selfAllocations)
+    });
 
     /**
      * When inverting call tree, the costs and times are dependent on position
      * in the tree. We must only count leaf nodes with self cost, and total costs
      * dependent on how many times the leaf node was found with a full stack path.
      *
      *   Total |  Self | Calls | Function
      * ============================================================================
      *  100%   |  100% |   100 | ▼ C
      *   50%   |   0%  |    50 |   ▼ B
      *   50%   |   0%  |    50 |     ▼ A
      *   50%   |   0%  |    50 |   ▼ B
      *
      * Every instance of a `CallView` represents a row in the call tree. The same
      * container node is used for all rows.
      */
-
-    // Leaf nodes in an inverted tree don't have to do anything special.
-    let isLeaf = this._level === 0;
-    let totalSamples = this.root.frame.samples;
-    let totalDuration = this.root.frame.duration;
-
-    // Self duration, cost
-    if (this.visibleCells.selfDuration) {
-      data.selfDuration = this.frame.youngestFrameSamples / totalSamples * totalDuration;
-    }
-    if (this.visibleCells.selfPercentage) {
-      data.selfPercentage = this.frame.youngestFrameSamples / totalSamples * 100;
-    }
-
-    // Total duration, cost
-    if (this.visibleCells.duration) {
-      data.totalDuration = this.frame.samples / totalSamples * totalDuration;
-    }
-    if (this.visibleCells.percentage) {
-      data.totalPercentage = this.frame.samples / totalSamples * 100;
-    }
-
-    // Raw samples.
-    if (this.visibleCells.samples) {
-      data.samples = this.frame.youngestFrameSamples;
-    }
-
-    // Self/total allocations count.
-    if (this.visibleCells.allocations) {
-      let childrenAllocations = this.frame.calls.reduce((acc, node) => acc + node.allocations, 0);
-      data.totalAllocations = this.frame.allocations + childrenAllocations;
-    }
-    if (this.visibleCells.selfAllocations) {
-      data.selfAllocations = this.frame.allocations;
-    }
-
-    // Frame name (function location or some meta information).
-    data.name = frameInfo.isMetaCategory
-      ? frameInfo.categoryData.label
-      : frameInfo.functionName || "";
-
-    data.tooltiptext = frameInfo.isMetaCategory
-      ? frameInfo.categoryData.label
-      : this.frame.location || "";
-
-    return this._cachedDisplayedData;
   },
 
   /**
    * Toggles the category information hidden or visible.
    * @param boolean visible
    */
   toggleCategories: function(visible) {
     if (!visible) {
--- a/browser/devtools/performance/performance-controller.js
+++ b/browser/devtools/performance/performance-controller.js
@@ -480,49 +480,38 @@ let PerformanceController = {
   /**
    * Returns the internal store of recording models.
    */
   getRecordings: function () {
     return this._recordings;
   },
 
   /**
-   * Utility method taking the currently selected recording item's features, or optionally passed
-   * in recording item, as well as the actor support on the server, returning a boolean
-   * indicating if the requirements pass or not. Used to toggle features' visibility mostly.
+   * Utility method taking a string or an array of strings of feature names (like
+   * "withAllocations" or "withMarkers"), and returns whether or not the current
+   * recording supports that feature, based off of UI preferences and server support.
    *
-   * @option {Array<string>} features
-   *         An array of strings indicating what configuration is needed on the recording
+   * @option {Array<string>|string} features
+   *         A string or array of strings indicating what configuration is needed on the recording
    *         model, like `withTicks`, or `withMemory`.
-   * @option {Array<string>} actors
-   *         An array of strings indicating what actors must exist.
-   * @option {boolean} mustBeCompleted
-   *         A boolean indicating whether the recording must be either completed or not.
-   *         Setting to undefined will allow either state.
-   * @param {RecordingModel} recording
-   *        An optional recording model to use instead of the currently selected.
    *
    * @return boolean
    */
-  isFeatureSupported: function ({ features, actors, mustBeCompleted }, recording) {
-    recording = recording || this.getCurrentRecording();
-    let recordingConfig = recording ? recording.getConfiguration() : {};
-    let currentCompletedState = recording ? recording.isCompleted() : void 0;
-    let actorsSupported = gFront.getActorSupport();
+  isFeatureSupported: function (features) {
+    if (!features) {
+      return true;
+    }
 
-    if (mustBeCompleted != null && mustBeCompleted !== currentCompletedState) {
+    let recording = this.getCurrentRecording();
+    if (!recording) {
       return false;
     }
-    if (actors && !actors.every(a => actorsSupported[a])) {
-      return false;
-    }
-    if (features && !features.every(f => recordingConfig[f])) {
-      return false;
-    }
-    return true;
+
+    let config = recording.getConfiguration();
+    return [].concat(features).every(f => config[f]);
   },
 
   /**
    * Returns an object with `supported` and `enabled` properties indicating
    * whether or not the platform is capable of turning on e10s and whether or not
    * it's already enabled, respectively.
    *
    * @return {object}
--- a/browser/devtools/performance/test/browser.ini
+++ b/browser/devtools/performance/test/browser.ini
@@ -22,16 +22,17 @@ support-files =
 [browser_perf-categories-js-calltree.js]
 [browser_perf-compatibility-01.js]
 [browser_perf-compatibility-02.js]
 [browser_perf-compatibility-03.js]
 [browser_perf-compatibility-04.js]
 [browser_perf-compatibility-05.js]
 [browser_perf-compatibility-06.js]
 [browser_perf-compatibility-07.js]
+[browser_perf-compatibility-08.js]
 [browser_perf-clear-01.js]
 [browser_perf-clear-02.js]
 [browser_perf-columns-js-calltree.js]
 [browser_perf-columns-memory-calltree.js]
 [browser_perf-console-record-01.js]
 [browser_perf-console-record-02.js]
 [browser_perf-console-record-03.js]
 [browser_perf-console-record-04.js]
--- a/browser/devtools/performance/test/browser_markers-cycle-collection.js
+++ b/browser/devtools/performance/test/browser_markers-cycle-collection.js
@@ -40,11 +40,15 @@ function* spawnTest () {
 
   yield Promise.all([
     waitForMarkerType(front, "nsCycleCollector::Collect"),
     waitForMarkerType(front, "nsCycleCollector::ForgetSkippable")
   ]);
   ok(true, "Got expected cycle collection events");
 
   yield front.stopRecording();
+
+  // Destroy the front before removing tab to ensure no
+  // lingering requests
+  yield front.destroy();
   yield removeTab(target.tab);
   finish();
 }
--- a/browser/devtools/performance/test/browser_markers-gc.js
+++ b/browser/devtools/performance/test/browser_markers-gc.js
@@ -30,16 +30,19 @@ function* spawnTest () {
   ok(markers.length > 0, "found atleast one GC marker");
   ok(markers.every(({start}) => typeof start === "number" && start > 0 && start < maxMarkerTime),
     "All markers have a start time between the valid range.");
   ok(markers.every(({end}) => typeof end === "number" && end > 0 && end < maxMarkerTime),
     "All markers have an end time between the valid range.");
   ok(markers.every(({causeName}) => typeof causeName === "string"),
     "All markers have a causeName.");
 
+  // Destroy the front before removing tab to ensure no
+  // lingering requests
+  yield front.destroy();
   yield removeTab(target.tab);
   finish();
 
   function handler (_, name, m) {
     if (name === "markers" && m[0].name === "GarbageCollection") {
       markers = m;
     }
   }
--- a/browser/devtools/performance/test/browser_markers-parse-html.js
+++ b/browser/devtools/performance/test/browser_markers-parse-html.js
@@ -20,16 +20,19 @@ function* spawnTest () {
 
   front.off("timeline-data", handler);
   yield front.stopRecording(model);
 
   info(`Got ${markers.length} markers.`);
 
   ok(markers.every(({name}) => name === "Parse HTML"), "All markers found are Parse HTML markers");
 
+  // Destroy the front before removing tab to ensure no
+  // lingering requests
+  yield front.destroy();
   yield removeTab(target.tab);
   finish();
 
   function handler (_, name, _markers) {
     if (name !== "markers") {
       return;
     }
 
--- a/browser/devtools/performance/test/browser_markers-styles.js
+++ b/browser/devtools/performance/test/browser_markers-styles.js
@@ -22,16 +22,19 @@ function* spawnTest () {
 
   info(`Got ${markers.length} markers.`);
 
   ok(markers.every(({name}) => name === "Styles"), "All markers found are Styles markers");
   ok(markers.length, "found some restyle markers");
 
   ok(markers.some(({restyleHint}) => restyleHint != void 0), "some markers have a restyleHint property");
 
+  // Destroy the front before removing tab to ensure no
+  // lingering requests
+  yield front.destroy();
   yield removeTab(target.tab);
   finish();
 
   function handler (_, name, m) {
     if (name === "markers") {
       markers = markers.concat(m.filter(marker => marker.name === "Styles"));
     }
   }
--- a/browser/devtools/performance/test/browser_markers-timestamp.js
+++ b/browser/devtools/performance/test/browser_markers-timestamp.js
@@ -31,16 +31,19 @@ function* spawnTest () {
   ok(markers.length === 2, "found 2 TimeStamp markers");
   ok(markers.every(({start}) => typeof start === "number" && start > 0 && start < maxMarkerTime),
     "All markers have a start time between the valid range.");
   ok(markers.every(({end}) => typeof end === "number" && end > 0 && end < maxMarkerTime),
     "All markers have an end time between the valid range.");
   is(markers[0].causeName, void 0, "Unlabeled timestamps have an empty causeName");
   is(markers[1].causeName, "myLabel", "Labeled timestamps have correct causeName");
 
+  // Destroy the front before removing tab to ensure no
+  // lingering requests
+  yield front.destroy();
   yield removeTab(target.tab);
   finish();
 
   function handler (_, name, m) {
     if (name === "markers") {
       markers = markers.concat(m.filter(marker => marker.name === "TimeStamp"));
     }
   }
--- a/browser/devtools/performance/test/browser_perf-compatibility-01.js
+++ b/browser/devtools/performance/test/browser_perf-compatibility-01.js
@@ -40,11 +40,14 @@ function* spawnTest() {
   yield front.stopRecording(recording);
 
   ok(typeof recording.getDuration() === "number",
     "The front.stopRecording() allows recording to get a duration.");
 
   ok(recording.getDuration() >= 0,
     "The profilerEndTime is after profilerStartTime.");
 
+  // Destroy the front before removing tab to ensure no
+  // lingering requests
+  yield front.destroy();
   yield removeTab(target.tab);
   finish();
 }
--- a/browser/devtools/performance/test/browser_perf-compatibility-02.js
+++ b/browser/devtools/performance/test/browser_perf-compatibility-02.js
@@ -61,18 +61,18 @@ let test = Task.async(function*() {
 
   is($("#overview-pane").hidden, true,
     "overview pane hidden when timeline mocked.");
 
   is($("#select-waterfall-view").hidden, true,
     "waterfall view button hidden when timeline mocked");
   is($("#select-js-calltree-view").hidden, false,
     "jscalltree view button not hidden when timeline/memory mocked");
-  is($("#select-js-flamegraph-view").hidden, true,
-    "jsflamegraph view button hidden when timeline mocked");
+  is($("#select-js-flamegraph-view").hidden, false,
+    "jsflamegraph view button not hidden when timeline mocked");
   is($("#select-memory-calltree-view").hidden, true,
     "memorycalltree view button hidden when memory mocked");
   is($("#select-memory-flamegraph-view").hidden, true,
     "memoryflamegraph view button hidden when memory mocked");
 
   ok(DetailsView.isViewSelected(JsCallTreeView),
     "JS Call Tree view selected by default when timeline/memory mocked.");
 
--- a/browser/devtools/performance/test/browser_perf-compatibility-03.js
+++ b/browser/devtools/performance/test/browser_perf-compatibility-03.js
@@ -39,11 +39,14 @@ function* spawnTest() {
   yield front.stopRecording(recording);
 
   ok(typeof recording.getDuration() === "number",
     "The front.stopRecording() allows recording to get a duration.");
 
   ok(recording.getDuration() >= 0,
     "The profilerEndTime is after profilerStartTime.");
 
+  // Destroy the front before removing tab to ensure no
+  // lingering requests
+  yield front.destroy();
   yield removeTab(target.tab);
   finish();
 }
--- a/browser/devtools/performance/test/browser_perf-compatibility-04.js
+++ b/browser/devtools/performance/test/browser_perf-compatibility-04.js
@@ -18,17 +18,16 @@ let test = Task.async(function*() {
 
   let { memory: memorySupport, timeline: timelineSupport } = gFront.getActorSupport();
   ok(!memorySupport, "memory should be mocked.");
   ok(timelineSupport, "timeline should not be mocked.");
 
   yield startRecording(panel);
   yield busyWait(100);
   yield waitUntil(() => PerformanceController.getCurrentRecording().getTicks().length);
-  yield waitUntil(() => PerformanceController.getCurrentRecording().getMemory().length);
   yield waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length);
   yield stopRecording(panel);
 
   let {
     label, duration, allocations, profile
   } = PerformanceController.getCurrentRecording().getAllData();
 
   is(label, "", "Empty label for mock.");
--- a/browser/devtools/performance/test/browser_perf-compatibility-06.js
+++ b/browser/devtools/performance/test/browser_perf-compatibility-06.js
@@ -22,11 +22,14 @@ function* spawnTest() {
     is(stats.position, void 0, "profiler-status has void `position`");
     count++;
   }
 
   is(model.getBufferUsage(), null, "model should have `null` for its buffer usage");
   yield front.stopRecording(model);
   is(model.getBufferUsage(), null, "after recording, model should still have `null` for its buffer usage");
 
+  // Destroy the front before removing tab to ensure no
+  // lingering requests
+  yield front.destroy();
   yield removeTab(target.tab);
   finish();
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-compatibility-08.js
@@ -0,0 +1,136 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that when setting recording features in the UI (like enabling framerate or memory),
+ * if the target does not support these features, then the target's support overrides
+ * the UI preferences when fetching configuration from a recording.
+ */
+
+const WAIT_TIME = 100;
+
+let test = Task.async(function*() {
+  yield testMockMemory();
+  yield testMockMemoryAndTimeline();
+  finish();
+});
+
+// Test mock memory
+function *testMockMemory () {
+  let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL, "performance", {
+    TEST_MOCK_MEMORY_ACTOR: true,
+  });
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
+  Services.prefs.setBoolPref(FRAMERATE_PREF, true);
+  Services.prefs.setBoolPref(ALLOCATIONS_PREF, true);
+  let { EVENTS, $, gFront, PerformanceController, PerformanceView, DetailsView, WaterfallView } = panel.panelWin;
+
+  let { memory: memorySupport, timeline: timelineSupport } = gFront.getActorSupport();
+  yield startRecording(panel, { waitForOverview: false });
+  yield waitUntil(() => PerformanceController.getCurrentRecording().getTicks().length);
+  yield waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length);
+  yield stopRecording(panel, { waitForOverview: false });
+
+  let config = PerformanceController.getCurrentRecording().getConfiguration();
+  let {
+    markers, allocations, memory, ticks
+  } = PerformanceController.getCurrentRecording().getAllData();
+
+  ok(typeof config.bufferSize === "number", "sanity check, config options contains `bufferSize`.");
+
+  is(config.withMemory, false,
+    "Recording configuration set by target's support, not by UI prefs [No Memory Actor: withMemory]");
+  is(config.withAllocations, false,
+    "Recording configuration set by target's support, not by UI prefs [No Memory Actor: withAllocations]");
+
+  is(config.withMarkers, true,
+    "Recording configuration set by target's support, not by UI prefs [No Memory Actor: withMarkers]");
+  is(config.withTicks, true,
+    "Recording configuration set by target's support, not by UI prefs [No Memory Actor: withTicks]");
+
+  ok(markers.length > 0, "markers exist.");
+  ok(ticks.length > 0, "ticks exist.");
+  isEmptyArray(memory, "memory");
+  isEmptyArray(allocations.sites, "allocations.sites");
+  isEmptyArray(allocations.timestamps, "allocations.timestamps");
+  isEmptyArray(allocations.frames, "allocations.frames");
+  isEmptyArray(allocations.counts, "allocations.counts");
+
+  is($("#overview-pane").hidden, false,
+    "overview pane not hidden when server not supporting memory actors, yet UI prefs request them.");
+  is($("#select-waterfall-view").hidden, false,
+    "waterfall view button not hidden when memory mocked, and UI prefs enable them");
+  is($("#select-js-calltree-view").hidden, false,
+    "jscalltree view button not hidden when memory mocked, and UI prefs enable them");
+  is($("#select-js-flamegraph-view").hidden, false,
+    "jsflamegraph view button not hidden when memory mocked, and UI prefs enable them");
+  is($("#select-memory-calltree-view").hidden, true,
+    "memorycalltree view button hidden when memory mocked, and UI prefs enable them");
+  is($("#select-memory-flamegraph-view").hidden, true,
+    "memoryflamegraph view button hidden when memory mocked, and UI prefs enable them");
+
+  yield gFront.destroy();
+  yield teardown(panel);
+}
+
+// Test mock memory and timeline actor
+function *testMockMemoryAndTimeline() {
+  let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL, "performance", {
+    TEST_MOCK_MEMORY_ACTOR: true,
+    TEST_MOCK_TIMELINE_ACTOR: true,
+  });
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
+  Services.prefs.setBoolPref(FRAMERATE_PREF, true);
+  Services.prefs.setBoolPref(ALLOCATIONS_PREF, true);
+  let { EVENTS, $, gFront, PerformanceController, PerformanceView, DetailsView, WaterfallView } = panel.panelWin;
+
+  let { memory: memorySupport, timeline: timelineSupport } = gFront.getActorSupport();
+  yield startRecording(panel, { waitForOverview: false });
+  yield busyWait(WAIT_TIME);
+  yield stopRecording(panel, { waitForOverview: false });
+
+  let config = PerformanceController.getCurrentRecording().getConfiguration();
+  let {
+    markers, allocations, memory, ticks
+  } = PerformanceController.getCurrentRecording().getAllData();
+
+  ok(typeof config.bufferSize === "number", "sanity check, config options contains `bufferSize`.");
+
+  is(config.withMemory, false,
+    "Recording configuration set by target's support, not by UI prefs [No Memory/Timeline Actor: withMemory]");
+  is(config.withAllocations, false,
+    "Recording configuration set by target's support, not by UI prefs [No Memory/Timeline Actor: withAllocations]");
+
+  is(config.withMarkers, false,
+    "Recording configuration set by target's support, not by UI prefs [No Memory/Timeline Actor: withMarkers]");
+  is(config.withTicks, false,
+    "Recording configuration set by target's support, not by UI prefs [No Memory/Timeline Actor: withTicks]");
+  isEmptyArray(markers, "markers");
+  isEmptyArray(ticks, "ticks");
+  isEmptyArray(memory, "memory");
+  isEmptyArray(allocations.sites, "allocations.sites");
+  isEmptyArray(allocations.timestamps, "allocations.timestamps");
+  isEmptyArray(allocations.frames, "allocations.frames");
+  isEmptyArray(allocations.counts, "allocations.counts");
+
+  is($("#overview-pane").hidden, true,
+    "overview pane hidden when server not supporting memory/timeline actors, yet UI prefs request them.");
+  is($("#select-waterfall-view").hidden, true,
+    "waterfall view button hidden when memory/timeline mocked, and UI prefs enable them");
+  is($("#select-js-calltree-view").hidden, false,
+    "jscalltree view button not hidden when memory/timeline mocked, and UI prefs enable them");
+  is($("#select-js-flamegraph-view").hidden, false,
+    "jsflamegraph view button not hidden when memory/timeline mocked, and UI prefs enable them");
+  is($("#select-memory-calltree-view").hidden, true,
+    "memorycalltree view button hidden when memory/timeline mocked, and UI prefs enable them");
+  is($("#select-memory-flamegraph-view").hidden, true,
+    "memoryflamegraph view button hidden when memory/timeline mocked, and UI prefs enable them");
+
+  yield gFront.destroy();
+  yield teardown(panel);
+}
+
+function isEmptyArray (array, name) {
+  ok(Array.isArray(array), `${name} is an array`);
+  is(array.length, 0, `${name} is empty`);
+}
--- a/browser/devtools/performance/test/browser_perf-data-massaging-01.js
+++ b/browser/devtools/performance/test/browser_perf-data-massaging-01.js
@@ -32,18 +32,16 @@ function* spawnTest() {
   info("Started profiling at: " + secondRecordingStartTime);
 
   busyWait(WAIT_TIME); // allow the profiler module to sample more cpu activity
 
   yield front.stopRecording(secondRecording);
   let secondRecordingProfile = secondRecording.getProfile();
   let secondRecordingSamples = secondRecordingProfile.threads[0].samples.data;
 
-  isnot(secondRecording._profilerStartTime, 0,
-    "The profiling start time should not be 0 on the second recording.");
   ok(secondRecording.getDuration() >= WAIT_TIME,
     "The second recording duration is correct.");
 
   const TIME_SLOT = secondRecordingProfile.threads[0].samples.schema.time;
   ok(secondRecordingSamples[0][TIME_SLOT] < secondRecordingStartTime,
     "The second recorded sample times were normalized.");
   ok(secondRecordingSamples[0][TIME_SLOT] > 0,
     "The second recorded sample times were normalized correctly.");
--- a/browser/devtools/performance/test/browser_perf-front-01.js
+++ b/browser/devtools/performance/test/browser_perf-front-01.js
@@ -42,11 +42,14 @@ function* spawnTest() {
   ok(typeof recording.getDuration() === "number",
     "The front.stopRecording() gives the recording model a stop time and duration.");
   ok(recording.getDuration() > 0,
     "The front.stopRecording() gives a positive duration amount.");
 
   is((yield front._request("memory", "getState")), "detached",
     "Memory actor is detached when stopping recording with allocations.");
 
+  // Destroy the front before removing tab to ensure no
+  // lingering requests
+  yield front.destroy();
   yield removeTab(target.tab);
   finish();
 }
--- a/browser/devtools/performance/test/browser_perf-front-02.js
+++ b/browser/devtools/performance/test/browser_perf-front-02.js
@@ -25,11 +25,14 @@ function* spawnTest() {
   info(timelineStart2);
   ok(typeof timelineStart1 === "number", "first timeline start returned a number");
   ok(typeof timelineStart2 === "number", "second timeline start returned a number");
   ok(typeof memoryStart1 === "number", "first memory start returned a number");
   ok(typeof memoryStart2 === "number", "second memory start returned a number");
   ok(timelineStart1 < timelineStart2, "can start timeline actor twice and get different start times");
   ok(memoryStart1 < memoryStart2, "can start memory actor twice and get different start times");
 
+  // Destroy the front before removing tab to ensure no
+  // lingering requests
+  yield front.destroy();
   yield removeTab(target.tab);
   finish();
 }
--- a/browser/devtools/performance/test/browser_perf-front-basic-profiler-01.js
+++ b/browser/devtools/performance/test/browser_perf-front-basic-profiler-01.js
@@ -22,11 +22,14 @@ function* spawnTest() {
 
   yield busyWait(WAIT_TIME);
 
   let stopModel = yield front.stopRecording(startModel);
 
   ok(stopModel.getProfile(), "recording model has a profile after stopping.");
   ok(stopModel.getDuration(), "recording model has a duration after stopping.");
 
+  // Destroy the front before removing tab to ensure no
+  // lingering requests
+  yield front.destroy();
   yield removeTab(target.tab);
   finish();
 }
--- a/browser/devtools/performance/test/browser_perf-front-basic-timeline-01.js
+++ b/browser/devtools/performance/test/browser_perf-front-basic-timeline-01.js
@@ -29,16 +29,19 @@ function* spawnTest() {
   yield Promise.all(Object.keys(deferreds).map(type => deferreds[type].promise));
   yield front.stopRecording();
   front.off("timeline-data", handler);
 
   is(counters.markers.length, 1, "one marker event fired.");
   is(counters.memory.length, 3, "three memory events fired.");
   is(counters.ticks.length, 3, "three ticks events fired.");
 
+  // Destroy the front before removing tab to ensure no
+  // lingering requests
+  yield front.destroy();
   yield removeTab(target.tab);
   finish();
 
   function handler (_, name, ...args) {
     if (name === "markers") {
       if (counters.markers.length >= 1) { return; }
       let [markers] = args;
       ok(markers[0].start, "received atleast one marker with `start`");
--- a/browser/devtools/performance/test/browser_perf-recording-model-02.js
+++ b/browser/devtools/performance/test/browser_perf-recording-model-02.js
@@ -33,11 +33,14 @@ function* spawnTest() {
   }
 
   ok(checkCount >= 1, "atleast 1 event were fired until the buffer was filled");
   is(lastBufferStatus, 1, "buffer usage cannot surpass 100%");
   yield front.stopRecording(model);
 
   is(model.getBufferUsage(), null, "getBufferUsage() should be null when no longer recording.");
 
+  // Destroy the front before removing tab to ensure no
+  // lingering requests
+  yield front.destroy();
   yield removeTab(target.tab);
   finish();
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/unit/test_tree-model-10.js
@@ -0,0 +1,149 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that the tree model calculates correct costs/percentages for
+ * frame nodes. The model-only version of browser_profiler-tree-view-10.js
+ */
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function () {
+  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  let thread = new ThreadNode(gThread, { invertTree: true, startTime: 0, endTime: 50 });
+
+  /**
+   * Samples
+   *
+   * A->C
+   * A->B
+   * A->B->C x4
+   * A->B->D x4
+   *
+   * Expected Tree
+   * +--total--+--self--+--tree-------------+
+   * |   50%   |   50%  |  C
+   * |   40%   |   0    |  -> B
+   * |   30%   |   0    |     -> A
+   * |   10%   |   0    |  -> A
+   *
+   * |   40%   |   40%  |  D
+   * |   40%   |   0    |  -> B
+   * |   40%   |   0    |     -> A
+   *
+   * |   10%   |   10%  |  B
+   * |   10%   |   0    |  -> A
+   */
+
+  [ // total, self, name
+    [ 50, 50, "C", [
+      [ 40,  0, "B", [
+        [ 30,  0, "A"]
+      ]],
+      [ 10,  0, "A"]
+    ]],
+    [ 40, 40, "D", [
+      [ 40,  0, "B", [
+        [ 40,  0, "A"],
+      ]]
+    ]],
+    [ 10, 10, "B", [
+      [ 10,  0, "A"],
+    ]]
+  ].forEach(compareFrameInfo(thread));
+});
+
+function compareFrameInfo (root, parent) {
+  parent = parent || root;
+  return function (def) {
+    let [total, self, name, children] = def;
+    let node = getFrameNodePath(parent, name);
+    let data = node.getInfo({ root });
+    equal(total, data.totalPercentage, `${name} has correct total percentage: ${data.totalPercentage}`);
+    equal(self, data.selfPercentage, `${name} has correct self percentage: ${data.selfPercentage}`);
+    if (children) {
+      children.forEach(compareFrameInfo(root, node));
+    }
+  }
+}
+
+let gThread = synthesizeProfileForTest([{
+  time: 5,
+  frames: [
+    { location: "(root)" },
+    { location: "A" },
+    { location: "B" },
+    { location: "C" }
+  ]
+}, {
+  time: 10,
+  frames: [
+    { location: "(root)" },
+    { location: "A" },
+    { location: "B" },
+    { location: "D" }
+  ]
+}, {
+  time: 15,
+  frames: [
+    { location: "(root)" },
+    { location: "A" },
+    { location: "C" },
+  ]
+}, {
+  time: 20,
+  frames: [
+    { location: "(root)" },
+    { location: "A" },
+    { location: "B" },
+  ]
+}, {
+  time: 25,
+  frames: [
+    { location: "(root)" },
+    { location: "A" },
+    { location: "B" },
+    { location: "C" }
+  ]
+}, {
+  time: 30,
+  frames: [
+    { location: "(root)" },
+    { location: "A" },
+    { location: "B" },
+    { location: "C" }
+  ]
+}, {
+  time: 35,
+  frames: [
+    { location: "(root)" },
+    { location: "A" },
+    { location: "B" },
+    { location: "D" }
+  ]
+}, {
+  time: 40,
+  frames: [
+    { location: "(root)" },
+    { location: "A" },
+    { location: "B" },
+    { location: "D" }
+  ]
+}, {
+  time: 45,
+  frames: [
+    { location: "(root)" },
+    { location: "B" },
+    { location: "C" }
+  ]
+}, {
+  time: 50,
+  frames: [
+    { location: "(root)" },
+    { location: "A" },
+    { location: "B" },
+    { location: "D" }
+  ]
+}]);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/unit/test_tree-model-11.js
@@ -0,0 +1,85 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that the costs for recursive frames does not overcount the collapsed
+ * samples.
+ */
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function () {
+  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  let thread = new ThreadNode(gThread, { startTime: 0, endTime: 50, flattenRecursion: true });
+
+  /**
+   * Samples
+   *
+   * A->B->C
+   * A->B->B->B->C
+   * A->B
+   * A->B->B->B
+   */
+
+  [ // total, self, name
+    [ 100, 0, "(root)", [
+      [ 100, 0, "A", [
+        [ 100, 50, "B", [
+          [ 50, 50, "C"]
+        ]]
+      ]],
+    ]],
+  ].forEach(compareFrameInfo(thread));
+});
+
+function compareFrameInfo (root, parent) {
+  parent = parent || root;
+  return function (def) {
+    let [total, self, name, children] = def;
+    let node = getFrameNodePath(parent, name);
+    let data = node.getInfo({ root });
+    equal(total, data.totalPercentage, `${name} has correct total percentage: ${data.totalPercentage}`);
+    equal(self, data.selfPercentage, `${name} has correct self percentage: ${data.selfPercentage}`);
+    if (children) {
+      children.forEach(compareFrameInfo(root, node));
+    }
+  }
+}
+
+let gThread = synthesizeProfileForTest([{
+  time: 5,
+  frames: [
+    { location: "(root)" },
+    { location: "A" },
+    { location: "B" },
+    { location: "B" },
+    { location: "B" },
+    { location: "C" }
+  ]
+}, {
+  time: 10,
+  frames: [
+    { location: "(root)" },
+    { location: "A" },
+    { location: "B" },
+    { location: "C" }
+  ]
+}, {
+  time: 15,
+  frames: [
+    { location: "(root)" },
+    { location: "A" },
+    { location: "B" },
+    { location: "B" },
+    { location: "B" },
+  ]
+}, {
+  time: 20,
+  frames: [
+    { location: "(root)" },
+    { location: "A" },
+    { location: "B" },
+  ]
+}]);
--- a/browser/devtools/performance/test/unit/test_waterfall-utils-collapse-01.js
+++ b/browser/devtools/performance/test/unit/test_waterfall-utils-collapse-01.js
@@ -1,67 +1,70 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the waterfall collapsing logic works properly.
  */
 
-function test() {
+function run_test() {
+  run_next_test();
+}
+
+add_task(function test() {
   const WaterfallUtils = devtools.require("devtools/performance/waterfall-utils");
 
-  let rootMarkerNode = WaterfallUtils.makeParentMarkerNode({ name: "(root)" });
+  let rootMarkerNode = WaterfallUtils.createParentNode({ name: "(root)" });
 
   WaterfallUtils.collapseMarkersIntoNode({
-    markerNode: rootMarkerNode,
+    rootNode: rootMarkerNode,
     markersList: gTestMarkers
   });
 
   function compare (marker, expected) {
     for (let prop in expected) {
       if (prop === "submarkers") {
         for (let i = 0; i < expected.submarkers.length; i++) {
           compare(marker.submarkers[i], expected.submarkers[i]);
         }
       } else if (prop !== "uid") {
-        is(marker[prop], expected[prop], `${expected.name} matches ${prop}`);
+        equal(marker[prop], expected[prop], `${expected.name} matches ${prop}`);
       }
     }
   }
 
   compare(rootMarkerNode, gExpectedOutput);
-  finish();
-}
+});
 
 const gTestMarkers = [
   { start: 1, end: 18, name: "DOMEvent" },
     // Test that JS markers can fold in DOM events and have marker children
     { start: 2, end: 16, name: "Javascript" },
       // Test all these markers can be children
       { start: 3, end: 4, name: "Paint" },
       { start: 5, end: 6, name: "Reflow" },
       { start: 7, end: 8, name: "Styles" },
       { start: 9, end: 9, name: "TimeStamp" },
       { start: 10, end: 11, name: "Parse HTML" },
       { start: 12, end: 13, name: "Parse XML" },
       { start: 14, end: 15, name: "GarbageCollection" },
   // Test that JS markers can be parents without being a child of DOM events
-  { start: 25, end: 30, name: "JavaScript" },
+  { start: 25, end: 30, name: "Javascript" },
     { start: 26, end: 27, name: "Paint" },
 ];
 
 const gExpectedOutput = {
   name: "(root)", submarkers: [
     { start: 1, end: 18, name: "DOMEvent", submarkers: [
       { start: 2, end: 16, name: "Javascript", submarkers: [
         { start: 3, end: 4, name: "Paint" },
         { start: 5, end: 6, name: "Reflow" },
         { start: 7, end: 8, name: "Styles" },
         { start: 9, end: 9, name: "TimeStamp" },
         { start: 10, end: 11, name: "Parse HTML" },
         { start: 12, end: 13, name: "Parse XML" },
         { start: 14, end: 15, name: "GarbageCollection" },
       ]}
     ]},
-    { start: 25, end: 30, name: "JavaScript", submarkers: [
+    { start: 25, end: 30, name: "Javascript", submarkers: [
       { start: 26, end: 27, name: "Paint" },
     ]}
 ]};
--- a/browser/devtools/performance/test/unit/test_waterfall-utils-collapse-02.js
+++ b/browser/devtools/performance/test/unit/test_waterfall-utils-collapse-02.js
@@ -1,41 +1,44 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the waterfall collapsing logic works properly for console.time/console.timeEnd
  * markers, as they should ignore any sort of collapsing.
  */
 
-function test() {
+function run_test() {
+  run_next_test();
+}
+
+add_task(function test() {
   const WaterfallUtils = devtools.require("devtools/performance/waterfall-utils");
 
-  let rootMarkerNode = WaterfallUtils.makeParentMarkerNode({ name: "(root)" });
+  let rootMarkerNode = WaterfallUtils.createParentNode({ name: "(root)" });
 
   WaterfallUtils.collapseMarkersIntoNode({
-    markerNode: rootMarkerNode,
+    rootNode: rootMarkerNode,
     markersList: gTestMarkers
   });
 
   function compare (marker, expected) {
     for (let prop in expected) {
       if (prop === "submarkers") {
         for (let i = 0; i < expected.submarkers.length; i++) {
           compare(marker.submarkers[i], expected.submarkers[i]);
         }
       } else if (prop !== "uid") {
-        is(marker[prop], expected[prop], `${expected.name} matches ${prop}`);
+        equal(marker[prop], expected[prop], `${expected.name} matches ${prop}`);
       }
     }
   }
 
   compare(rootMarkerNode, gExpectedOutput);
-  finish();
-}
+});
 
 const gTestMarkers = [
   { start: 2, end: 9, name: "Javascript" },
     { start: 3, end: 4, name: "Paint" },
   // Time range starting in nest, ending outside
   { start: 5, end: 12, name: "ConsoleTime", causeName: "1" },
 
   // Time range starting outside of nest, ending inside
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/unit/test_waterfall-utils-collapse-03.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that the waterfall collapsing works when atleast two
+ * collapsible markers downward, and the following marker is outside of both ranges.
+ */
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function test() {
+  const WaterfallUtils = devtools.require("devtools/performance/waterfall-utils");
+
+  let rootMarkerNode = WaterfallUtils.createParentNode({ name: "(root)" });
+
+  WaterfallUtils.collapseMarkersIntoNode({
+    rootNode: rootMarkerNode,