Merge m-c to oak
authorRobert Strong <robert.bugzilla@gmail.com>
Sun, 19 Jul 2015 20:08:59 -0700
changeset 491459 4b877e931fa26fc595ffdf3ce49e1c3f5e8d4189
parent 491458 08f8bc16527c1d74ad1f97f8a7389f17338506ef (current diff)
parent 279545 9c919ce631eaa97db0ca1d001891eb2f159636a8 (diff)
child 491460 78280e845a914fcbf49c742134f6375087377ce7
push id47343
push userbmo:dothayer@mozilla.com
push dateWed, 01 Mar 2017 22:58:58 +0000
milestone42.0a1
Merge m-c to oak
CLOBBER
b2g/app/Makefile.in
b2g/installer/Makefile.in
b2g/installer/package-manifest.in
browser/app/Makefile.in
browser/app/profile/firefox.js
browser/installer/package-manifest.in
browser/themes/linux/urlbar-arrow.png
browser/themes/linux/urlbar-arrow@2x.png
browser/themes/osx/urlbar-arrow.png
browser/themes/osx/urlbar-arrow@2x.png
browser/themes/windows/urlbar-arrow.png
browser/themes/windows/urlbar-arrow@2x.png
build/mobile/robocop/robotium-solo-5.4.1.jar
configure.in
dom/interfaces/base/nsIDOMJSWindow.idl
dom/manifest/ImageObjectProcessor.jsm
dom/manifest/ManifestFinder.jsm
dom/manifest/ManifestObtainer.jsm
dom/manifest/ManifestProcessor.jsm
dom/manifest/ValueExtractor.jsm
dom/manifest/test/browser_hasManifestLink.js
dom/media/MediaPromise.h
dom/media/MediaTaskQueue.cpp
dom/media/MediaTaskQueue.h
dom/media/gtest/TestMediaPromise.cpp
dom/system/gonk/tests/test_ril_worker_mmi.js
dom/system/gonk/tests/test_ril_worker_mmi_cf.js
dom/telephony/test/marionette/test_mmi.js
extensions/cookie/test/unit/test_permmanager_mailto.js
js/src/jsreflect.cpp
js/src/jsreflect.h
js/xpconnect/src/XPCShellImpl.cpp
mobile/android/base/resources/drawable/anchored_popup_bg.xml
testing/mach_commands.py
testing/mochitest/mochitest_options.py
testing/mochitest/runtests.py
testing/mozbase/docs/mozlog_structured.rst
testing/mozbase/mozlog/mozlog/logger.py
testing/mozbase/mozlog/mozlog/loggingmixin.py
testing/mozbase/mozlog/mozlog/loglistener.py
testing/mozbase/mozlog/mozlog/structured/__init__.py
testing/mozbase/mozlog/mozlog/structured/commandline.py
testing/mozbase/mozlog/mozlog/structured/formatters/__init__.py
testing/mozbase/mozlog/mozlog/structured/formatters/base.py
testing/mozbase/mozlog/mozlog/structured/formatters/errorsummary.py
testing/mozbase/mozlog/mozlog/structured/formatters/html/__init__.py
testing/mozbase/mozlog/mozlog/structured/formatters/html/html.py
testing/mozbase/mozlog/mozlog/structured/formatters/html/main.js
testing/mozbase/mozlog/mozlog/structured/formatters/html/style.css
testing/mozbase/mozlog/mozlog/structured/formatters/html/xmlgen.py
testing/mozbase/mozlog/mozlog/structured/formatters/machformatter.py
testing/mozbase/mozlog/mozlog/structured/formatters/tbplformatter.py
testing/mozbase/mozlog/mozlog/structured/formatters/unittest.py
testing/mozbase/mozlog/mozlog/structured/formatters/xunit.py
testing/mozbase/mozlog/mozlog/structured/handlers/__init__.py
testing/mozbase/mozlog/mozlog/structured/handlers/base.py
testing/mozbase/mozlog/mozlog/structured/handlers/bufferhandler.py
testing/mozbase/mozlog/mozlog/structured/handlers/statushandler.py
testing/mozbase/mozlog/mozlog/structured/logtypes.py
testing/mozbase/mozlog/mozlog/structured/reader.py
testing/mozbase/mozlog/mozlog/structured/scripts/__init__.py
testing/mozbase/mozlog/mozlog/structured/scripts/format.py
testing/mozbase/mozlog/mozlog/structured/scripts/logmerge.py
testing/mozbase/mozlog/mozlog/structured/scripts/unstable.py
testing/mozbase/mozlog/mozlog/structured/stdadapter.py
testing/mozbase/mozlog/mozlog/structured/structuredlog.py
testing/runcppunittests.py
testing/runtimes/linux64-asan/mochitest-browser-chrome.runtimes.json
testing/runtimes/linux64-asan/mochitest-e10s-browser-chrome.runtimes.json
testing/runtimes/win32-pgo/mochitest-e10s-browser-chrome.runtimes.json
testing/xpcshell/runxpcshelltests.py
toolkit/components/telemetry/Histograms.json
toolkit/devtools/touch-events.js
toolkit/modules/PromiseMessage.jsm
toolkit/mozapps/installer/packager.mk
toolkit/xre/nsAppRunner.cpp
--- a/accessible/atk/nsMaiInterfaceText.cpp
+++ b/accessible/atk/nsMaiInterfaceText.cpp
@@ -158,58 +158,63 @@ getTextCB(AtkText *aText, gint aStartOff
   return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
 }
 
 static gchar*
 getTextAfterOffsetCB(AtkText *aText, gint aOffset,
                      AtkTextBoundary aBoundaryType,
                      gint *aStartOffset, gint *aEndOffset)
 {
+    nsAutoString autoStr;
+  int32_t startOffset = 0, endOffset = 0;
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
-  if (!accWrap)
-    return nullptr;
+  if (accWrap) {
+    HyperTextAccessible* text = accWrap->AsHyperText();
+    if (!text || !text->IsTextRole())
+      return nullptr;
 
-  HyperTextAccessible* text = accWrap->AsHyperText();
-  if (!text || !text->IsTextRole())
-    return nullptr;
-
-  nsAutoString autoStr;
-  int32_t startOffset = 0, endOffset = 0;
-  text->TextAfterOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr);
+    text->TextAfterOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr);
+    ConvertTexttoAsterisks(accWrap, autoStr);
+  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+    proxy->GetTextAfterOffset(aOffset, aBoundaryType, autoStr, &startOffset,
+                              &endOffset);
+  }
 
   *aStartOffset = startOffset;
   *aEndOffset = endOffset;
 
-  ConvertTexttoAsterisks(accWrap, autoStr);
   NS_ConvertUTF16toUTF8 cautoStr(autoStr);
   return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
 }
 
 static gchar*
 getTextAtOffsetCB(AtkText *aText, gint aOffset,
                   AtkTextBoundary aBoundaryType,
                   gint *aStartOffset, gint *aEndOffset)
 {
+  nsAutoString autoStr;
+  int32_t startOffset = 0, endOffset = 0;
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
-  if (!accWrap)
-    return nullptr;
-
-  HyperTextAccessible* text = accWrap->AsHyperText();
-  if (!text || !text->IsTextRole())
-    return nullptr;
+  if (accWrap) {
+    HyperTextAccessible* text = accWrap->AsHyperText();
+    if (!text || !text->IsTextRole())
+      return nullptr;
 
-    nsAutoString autoStr;
-    int32_t startOffset = 0, endOffset = 0;
     text->TextAtOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr);
-    *aStartOffset = startOffset;
-    *aEndOffset = endOffset;
+    ConvertTexttoAsterisks(accWrap, autoStr);
+  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+    proxy->GetTextAtOffset(aOffset, aBoundaryType, autoStr, &startOffset,
+                           &endOffset);
+  }
 
-    ConvertTexttoAsterisks(accWrap, autoStr);
-    NS_ConvertUTF16toUTF8 cautoStr(autoStr);
-    return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
+  *aStartOffset = startOffset;
+  *aEndOffset = endOffset;
+
+  NS_ConvertUTF16toUTF8 cautoStr(autoStr);
+  return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
 }
 
 static gunichar
 getCharacterAtOffsetCB(AtkText* aText, gint aOffset)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
   if (accWrap) {
     HyperTextAccessible* text = accWrap->AsHyperText();
@@ -228,32 +233,35 @@ getCharacterAtOffsetCB(AtkText* aText, g
   return 0;
 }
 
 static gchar*
 getTextBeforeOffsetCB(AtkText *aText, gint aOffset,
                       AtkTextBoundary aBoundaryType,
                       gint *aStartOffset, gint *aEndOffset)
 {
-  AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
-  if (!accWrap)
-    return nullptr;
-
-  HyperTextAccessible* text = accWrap->AsHyperText();
-  if (!text || !text->IsTextRole())
-    return nullptr;
-
   nsAutoString autoStr;
   int32_t startOffset = 0, endOffset = 0;
-  text->TextBeforeOffset(aOffset, aBoundaryType,
-                         &startOffset, &endOffset, autoStr);
+  AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
+  if (accWrap) {
+    HyperTextAccessible* text = accWrap->AsHyperText();
+    if (!text || !text->IsTextRole())
+      return nullptr;
+
+    text->TextBeforeOffset(aOffset, aBoundaryType,
+                           &startOffset, &endOffset, autoStr);
+    ConvertTexttoAsterisks(accWrap, autoStr);
+  } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
+    proxy->GetTextBeforeOffset(aOffset, aBoundaryType, autoStr, &startOffset,
+                               &endOffset);
+  }
+
   *aStartOffset = startOffset;
   *aEndOffset = endOffset;
 
-  ConvertTexttoAsterisks(accWrap, autoStr);
   NS_ConvertUTF16toUTF8 cautoStr(autoStr);
   return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
 }
 
 static gint
 getCaretOffsetCB(AtkText *aText)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -25,16 +25,17 @@
 #include "nsContainerFrame.h"
 #include "nsFrameSelection.h"
 #include "nsILineIterator.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPersistentProperties2.h"
 #include "nsIScrollableFrame.h"
 #include "nsIServiceManager.h"
 #include "nsITextControlElement.h"
+#include "nsIMathMLFrame.h"
 #include "nsTextFragment.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/MathAlgorithms.h"
 #include "gfxSkipChars.h"
 #include <algorithm>
@@ -947,16 +948,155 @@ HyperTextAccessible::GetLevelInternal()
   if (mContent->IsHTMLElement(nsGkAtoms::h5))
     return 5;
   if (mContent->IsHTMLElement(nsGkAtoms::h6))
     return 6;
 
   return AccessibleWrap::GetLevelInternal();
 }
 
+void
+HyperTextAccessible::SetMathMLXMLRoles(nsIPersistentProperties* aAttributes)
+{
+  // Add MathML xmlroles based on the position inside the parent.
+  Accessible* parent = Parent();
+  if (parent) {
+    switch (parent->Role()) {
+    case roles::MATHML_CELL:
+    case roles::MATHML_ENCLOSED:
+    case roles::MATHML_ERROR:
+    case roles::MATHML_MATH:
+    case roles::MATHML_ROW:
+    case roles::MATHML_SQUARE_ROOT:
+    case roles::MATHML_STYLE:
+      if (Role() == roles::MATHML_OPERATOR) {
+        // This is an operator inside an <mrow> (or an inferred <mrow>).
+        // See http://www.w3.org/TR/MathML3/chapter3.html#presm.inferredmrow
+        // XXX We should probably do something similar for MATHML_FENCED, but
+        // operators do not appear in the accessible tree. See bug 1175747.
+        nsIMathMLFrame* mathMLFrame = do_QueryFrame(GetFrame());
+        if (mathMLFrame) {
+          nsEmbellishData embellishData;
+          mathMLFrame->GetEmbellishData(embellishData);
+          if (NS_MATHML_EMBELLISH_IS_FENCE(embellishData.flags)) {
+            if (!PrevSibling()) {
+              nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                                     nsGkAtoms::open_fence);
+            } else if (!NextSibling()) {
+              nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                                     nsGkAtoms::close_fence);
+            }
+          }
+          if (NS_MATHML_EMBELLISH_IS_SEPARATOR(embellishData.flags)) {
+            nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                                   nsGkAtoms::separator_);
+          }
+        }
+      }
+    break;
+    case roles::MATHML_FRACTION:
+      nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                             IndexInParent() == 0 ?
+                             nsGkAtoms::numerator :
+                             nsGkAtoms::denominator);
+      break;
+    case roles::MATHML_ROOT:
+      nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                             IndexInParent() == 0 ? nsGkAtoms::base :
+                             nsGkAtoms::root_index);
+      break;
+    case roles::MATHML_SUB:
+      nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                             IndexInParent() == 0 ? nsGkAtoms::base :
+                             nsGkAtoms::subscript);
+      break;
+    case roles::MATHML_SUP:
+      nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                             IndexInParent() == 0 ? nsGkAtoms::base :
+                             nsGkAtoms::superscript);
+      break;
+    case roles::MATHML_SUB_SUP: {
+      int32_t index = IndexInParent();
+      nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                             index == 0 ? nsGkAtoms::base :
+                             (index == 1 ? nsGkAtoms::subscript :
+                              nsGkAtoms::superscript));
+    } break;
+    case roles::MATHML_UNDER:
+      nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                             IndexInParent() == 0 ? nsGkAtoms::base :
+                             nsGkAtoms::underscript);
+      break;
+    case roles::MATHML_OVER:
+      nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                             IndexInParent() == 0 ? nsGkAtoms::base :
+                             nsGkAtoms::overscript);
+      break;
+    case roles::MATHML_UNDER_OVER: {
+      int32_t index = IndexInParent();
+      nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                             index == 0 ? nsGkAtoms::base :
+                             (index == 1 ? nsGkAtoms::underscript :
+                              nsGkAtoms::overscript));
+    } break;
+    case roles::MATHML_MULTISCRIPTS: {
+      // Get the <multiscripts> base.
+      nsIContent* child;
+      bool baseFound = false;
+      for (child = parent->GetContent()->GetFirstChild(); child;
+           child = child->GetNextSibling()) {
+        if (child->IsMathMLElement()) {
+          baseFound = true;
+          break;
+        }
+      }
+      if (baseFound) {
+        nsIContent* content = GetContent();
+        if (child == content) {
+          // We are the base.
+          nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                                 nsGkAtoms::base);
+        } else {
+          // Browse the list of scripts to find us and determine our type.
+          bool postscript = true;
+          bool subscript = true;
+          for (child = child->GetNextSibling(); child;
+               child = child->GetNextSibling()) {
+            if (!child->IsMathMLElement())
+              continue;
+            if (child->IsMathMLElement(nsGkAtoms::mprescripts_)) {
+              postscript = false;
+              subscript = true;
+              continue;
+            }
+            if (child == content) {
+              if (postscript) {
+                nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                                       subscript ?
+                                       nsGkAtoms::subscript :
+                                       nsGkAtoms::superscript);
+              } else {
+                nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
+                                       subscript ?
+                                       nsGkAtoms::presubscript :
+                                       nsGkAtoms::presuperscript);
+              }
+              break;
+            }
+            subscript = !subscript;
+          }
+        }
+      }
+    } break;
+    default:
+      break;
+    }
+  }
+}
+
 already_AddRefed<nsIPersistentProperties>
 HyperTextAccessible::NativeAttributes()
 {
   nsCOMPtr<nsIPersistentProperties> attributes =
     AccessibleWrap::NativeAttributes();
 
   // 'formatting' attribute is deprecated, 'display' attribute should be
   // instead.
@@ -971,18 +1111,21 @@ HyperTextAccessible::NativeAttributes()
     int32_t lineNumber = CaretLineNumber();
     if (lineNumber >= 1) {
       nsAutoString strLineNumber;
       strLineNumber.AppendInt(lineNumber);
       nsAccUtils::SetAccAttr(attributes, nsGkAtoms::lineNumber, strLineNumber);
     }
   }
 
-  if (HasOwnContent())
+  if (HasOwnContent()) {
     GetAccService()->MarkupAttributes(mContent, attributes);
+    if (mContent->IsMathMLElement())
+      SetMathMLXMLRoles(attributes);
+  }
 
   return attributes.forget();
 }
 
 nsIAtom*
 HyperTextAccessible::LandmarkRole() const
 {
   if (!HasOwnContent())
--- a/accessible/generic/HyperTextAccessible.h
+++ b/accessible/generic/HyperTextAccessible.h
@@ -556,16 +556,22 @@ protected:
    * @param aStartOffset      [in, out] the start offset
    * @param aEndOffset        [in, out] the end offset
    * @param aAttributes       [out, optional] result attributes
    */
   void GetSpellTextAttr(nsINode* aNode, int32_t aNodeOffset,
                         uint32_t* aStartOffset, uint32_t* aEndOffset,
                         nsIPersistentProperties* aAttributes);
 
+  /**
+   * Set xml-roles attributes for MathML elements.
+   * @param aAttributes
+   */
+  void SetMathMLXMLRoles(nsIPersistentProperties* aAttributes);
+
 private:
   /**
    * End text offsets array.
    */
   mutable nsTArray<uint32_t> mOffsets;
 };
 
 
--- a/accessible/ipc/DocAccessibleChild.cpp
+++ b/accessible/ipc/DocAccessibleChild.cpp
@@ -1648,16 +1648,34 @@ DocAccessibleChild::RecvEmbeddedChildAt(
   if (!acc)
     return true;
 
   *aChildID = reinterpret_cast<uintptr_t>(acc->GetEmbeddedChildAt(aIdx));
   return true;
 }
 
 bool
+DocAccessibleChild::RecvFocusedChild(const uint64_t& aID,
+                                       uint64_t* aChild,
+                                       bool* aOk)
+{
+  *aChild = 0;
+  *aOk = false;
+  Accessible* acc = IdToAccessible(aID);
+  if (acc) {
+    Accessible* child = acc->FocusedChild();
+    if (child) {
+      *aChild = reinterpret_cast<uint64_t>(child->UniqueID());
+      *aOk = true;
+    }
+  }
+  return true;
+}
+
+bool
 DocAccessibleChild::RecvChildAtPoint(const uint64_t& aID,
                                      const int32_t& aX,
                                      const int32_t& aY,
                                      const uint32_t& aWhich,
                                      uint64_t* aChild,
                                      bool* aOk)
 {
   *aChild = 0;
--- a/accessible/ipc/DocAccessibleChild.h
+++ b/accessible/ipc/DocAccessibleChild.h
@@ -406,16 +406,20 @@ public:
 
   virtual bool RecvIndexOfEmbeddedChild(const uint64_t& aID,
                                         const uint64_t& aChildID,
                                         uint32_t* aChildIdx) override final;
 
   virtual bool RecvEmbeddedChildAt(const uint64_t& aID, const uint32_t& aIdx,
                                    uint64_t* aChildID) override final;
 
+  virtual bool RecvFocusedChild(const uint64_t& aID,
+                                uint64_t* aChild,
+                                bool* aOk) override;
+
   virtual bool RecvChildAtPoint(const uint64_t& aID,
                                 const int32_t& aX,
                                 const int32_t& aY,
                                 const uint32_t& aWhich,
                                 uint64_t* aChild,
                                 bool* aOk) override;
 
   virtual bool RecvBounds(const uint64_t& aID, nsIntRect* aRect) override;
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/PDocAccessible.ipdl
@@ -217,16 +217,18 @@ child:
   prio(high) sync MaxValue(uint64_t aID) returns(double aValue);
   prio(high) sync Step(uint64_t aID) returns(double aStep);
 
   prio(high) sync TakeFocus(uint64_t aID);
   prio(high) sync IndexOfEmbeddedChild(uint64_t aID, uint64_t aChildID)
     returns(uint32_t childIdx);
   prio(high) sync EmbeddedChildAt(uint64_t aID, uint32_t aChildIdx)
     returns(uint64_t aChild);
+  prio(high) sync FocusedChild(uint64_t aID)
+    returns(uint64_t aChild, bool aOk);
   prio(high) sync ChildAtPoint(uint64_t aID, int32_t aX, int32_t aY, uint32_t aWhich)
     returns(uint64_t aChild, bool aOk);
   prio(high) sync Bounds(uint64_t aID) returns(nsIntRect aRect);
 
   prio(high) sync Language(uint64_t aID) returns(nsString aLocale);
   prio(high) sync DocType(uint64_t aID) returns(nsString aType);
   prio(high) sync URL(uint64_t aID) returns(nsString aURL);
   prio(high) sync MimeType(uint64_t aID) returns(nsString aMime);
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/ProxyAccessible.cpp
@@ -515,25 +515,37 @@ uint32_t
 ProxyAccessible::RowExtent()
 {
   uint32_t extent = 0;
   unused << mDoc->SendRowExtent(mID, &extent);
   return extent;
 }
 
 void
-ProxyAccessible::ColHeaderCells(nsTArray<uint64_t>* aCells)
+ProxyAccessible::ColHeaderCells(nsTArray<ProxyAccessible*>* aCells)
 {
-  unused << mDoc->SendColHeaderCells(mID, aCells);
+  nsTArray<uint64_t> targetIDs;
+  unused << mDoc->SendColHeaderCells(mID, &targetIDs);
+
+  size_t targetCount = targetIDs.Length();
+  for (size_t i = 0; i < targetCount; i++) {
+    aCells->AppendElement(mDoc->GetAccessible(targetIDs[i]));
+  }
 }
 
 void
-ProxyAccessible::RowHeaderCells(nsTArray<uint64_t>* aCells)
+ProxyAccessible::RowHeaderCells(nsTArray<ProxyAccessible*>* aCells)
 {
-  unused << mDoc->SendRowHeaderCells(mID, aCells);
+  nsTArray<uint64_t> targetIDs;
+  unused << mDoc->SendRowHeaderCells(mID, &targetIDs);
+
+  size_t targetCount = targetIDs.Length();
+  for (size_t i = 0; i < targetCount; i++) {
+    aCells->AppendElement(mDoc->GetAccessible(targetIDs[i]));
+  }
 }
 
 bool
 ProxyAccessible::IsCellSelected()
 {
   bool selected = false;
   unused << mDoc->SendIsCellSelected(mID, &selected);
   return selected;
@@ -920,16 +932,25 @@ ProxyAccessible*
 ProxyAccessible::EmbeddedChildAt(size_t aChildIdx)
 {
   uint64_t childID;
   unused << mDoc->SendEmbeddedChildAt(mID, aChildIdx, &childID);
   return mDoc->GetAccessible(childID);
 }
 
 ProxyAccessible*
+ProxyAccessible::FocusedChild()
+{
+  uint64_t childID = 0;
+  bool ok = false;
+  unused << mDoc->SendFocusedChild(mID, &childID, &ok);
+  return ok ? mDoc->GetAccessible(childID) : nullptr;
+}
+
+ProxyAccessible*
 ProxyAccessible::ChildAtPoint(int32_t aX, int32_t aY,
                               Accessible::EWhichChildAtPoint aWhichChild)
 {
   uint64_t childID = 0;
   bool ok = false;
   unused << mDoc->SendChildAtPoint(mID, aX, aY,
                                    static_cast<uint32_t>(aWhichChild),
                                    &childID, &ok);
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -189,16 +189,22 @@ public:
   nsIntSize ImageSize();
 
   uint32_t StartOffset(bool* aOk);
 
   uint32_t EndOffset(bool* aOk);
 
   bool IsLinkValid();
 
+  // XXX checking mRole alone may not result in same behavior as Accessibles
+  // due to ARIA roles
+  inline bool IsTable() const { return mRole == roles::TABLE; }
+  inline bool IsTableRow() const { return mRole == roles::ROW; }
+  inline bool IsTableCell() const { return mRole == roles::CELL; }
+
   uint32_t AnchorCount(bool* aOk);
 
   void AnchorURIAt(uint32_t aIndex, nsCString& aURI, bool* aOk);
 
   ProxyAccessible* AnchorAt(uint32_t aIndex);
 
   uint32_t LinkCount();
 
@@ -213,19 +219,19 @@ public:
   uint32_t ColIdx();
 
   uint32_t RowIdx();
 
   uint32_t ColExtent();
 
   uint32_t RowExtent();
 
-  void ColHeaderCells(nsTArray<uint64_t>* aCells);
+  void ColHeaderCells(nsTArray<ProxyAccessible*>* aCells);
 
-  void RowHeaderCells(nsTArray<uint64_t>* aCells);
+  void RowHeaderCells(nsTArray<ProxyAccessible*>* aCells);
 
   bool IsCellSelected();
 
   ProxyAccessible* TableCaption();
   void TableSummary(nsString& aSummary);
   uint32_t TableColumnCount();
   uint32_t TableRowCount();
   ProxyAccessible* TableCellAt(uint32_t aRow, uint32_t aCol);
@@ -272,16 +278,17 @@ public:
 
   double CurValue();
   bool SetCurValue(double aValue);
   double MinValue();
   double MaxValue();
   double Step();
 
   void TakeFocus();
+  ProxyAccessible* FocusedChild();
   ProxyAccessible* ChildAtPoint(int32_t aX, int32_t aY,
                                 Accessible::EWhichChildAtPoint aWhichChild);
   nsIntRect Bounds();
 
   void Language(nsString& aLocale);
   void DocType(nsString& aType);
   void URL(nsString& aURL);
   void MimeType(nsString aMime);
--- a/accessible/mac/moz.build
+++ b/accessible/mac/moz.build
@@ -35,9 +35,11 @@ LOCAL_INCLUDES += [
     '/layout/generic',
     '/layout/xul',
     '/widget',
     '/widget/cocoa',
 ]
 
 FINAL_LIBRARY = 'xul'
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FAIL_ON_WARNINGS = True
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -13,16 +13,17 @@
 #include "nsIAccessibleRelation.h"
 #include "nsIAccessibleEditableText.h"
 #include "nsIPersistentProperties2.h"
 #include "Relation.h"
 #include "Role.h"
 #include "RootAccessible.h"
 #include "TableAccessible.h"
 #include "TableCellAccessible.h"
+#include "mozilla/a11y/PDocAccessible.h"
 
 #include "mozilla/Services.h"
 #include "nsRect.h"
 #include "nsCocoaUtils.h"
 #include "nsCoord.h"
 #include "nsObjCExceptions.h"
 #include "nsWhitespaceTokenizer.h"
 #include <prdtoa.h>
@@ -206,19 +207,24 @@ ConvertToNSArray(nsTArray<ProxyAccessibl
 
 - (BOOL)accessibilityIsIgnored
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   // unknown (either unimplemented, or irrelevant) elements are marked as ignored
   // as well as expired elements.
 
-  AccessibleWrap* accWrap = [self getGeckoAccessible];
-  return !accWrap || ([[self role] isEqualToString:NSAccessibilityUnknownRole] &&
-                               !(accWrap->InteractiveState() & states::FOCUSABLE));
+  bool noRole = [[self role] isEqualToString:NSAccessibilityUnknownRole];
+  if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+    return (noRole && !(accWrap->InteractiveState() & states::FOCUSABLE));
+
+  if (ProxyAccessible* proxy = [self getProxyAccessible])
+    return (noRole && !(proxy->State() & states::FOCUSABLE));
+
+  return true;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
 }
 
 - (NSArray*)additionalAccessibilityAttributeNames
 {
   NSMutableArray* additional = [NSMutableArray array];
   switch (mRole) {
@@ -264,17 +270,18 @@ ConvertToNSArray(nsTArray<ProxyAccessibl
 }
 
 - (NSArray*)accessibilityAttributeNames
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   // if we're expired, we don't support any attributes.
   AccessibleWrap* accWrap = [self getGeckoAccessible];
-  if (!accWrap)
+  ProxyAccessible* proxy = [self getProxyAccessible];
+  if (!accWrap && !proxy)
     return [NSArray array];
 
   static NSArray* generalAttributes = nil;
   static NSArray* tableAttrs = nil;
   static NSArray* tableRowAttrs = nil;
   static NSArray* tableCellAttrs = nil;
   NSMutableArray* tempArray = nil;
 
@@ -323,21 +330,21 @@ ConvertToNSArray(nsTArray<ProxyAccessibl
     [tempArray addObject:NSAccessibilityRowHeaderUIElementsAttribute];
     [tempArray addObject:NSAccessibilityColumnHeaderUIElementsAttribute];
     tableCellAttrs = [[NSArray alloc] initWithArray:tempArray];
     [tempArray release];
   }
 
   NSArray* objectAttributes = generalAttributes;
 
-  if (accWrap->IsTable())
+  if ((accWrap && accWrap->IsTable()) || (proxy && proxy->IsTable()))
     objectAttributes = tableAttrs;
-  else if (accWrap->IsTableRow())
+  else if ((accWrap && accWrap->IsTableRow()) || (proxy && proxy->IsTableRow()))
     objectAttributes = tableRowAttrs;
-  else if (accWrap->IsTableCell())
+  else if ((accWrap && accWrap->IsTableCell()) || (proxy && proxy->IsTableCell()))
     objectAttributes = tableCellAttrs;
 
   NSArray* additionalAttributes = [self additionalAccessibilityAttributeNames];
   if ([additionalAttributes count])
     objectAttributes = [objectAttributes arrayByAddingObjectsFromArray:additionalAttributes];
 
   return objectAttributes;
 
@@ -359,17 +366,18 @@ ConvertToNSArray(nsTArray<ProxyAccessibl
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (id)accessibilityAttributeValue:(NSString*)attribute
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   AccessibleWrap* accWrap = [self getGeckoAccessible];
-  if (!accWrap)
+  ProxyAccessible* proxy = [self getProxyAccessible];
+  if (!accWrap && !proxy)
     return nil;
 
 #if DEBUG
   if ([attribute isEqualToString:@"AXMozDescription"])
     return [NSString stringWithFormat:@"role = %u native = %@", mRole, [self class]];
 #endif
 
   if ([attribute isEqualToString:NSAccessibilityChildrenAttribute])
@@ -401,72 +409,127 @@ ConvertToNSArray(nsTArray<ProxyAccessibl
     return [self size];
   if ([attribute isEqualToString:NSAccessibilityWindowAttribute])
     return [self window];
   if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute])
     return [self window];
   if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
     return [self title];
   if ([attribute isEqualToString:NSAccessibilityTitleUIElementAttribute]) {
-    Relation rel =
-      accWrap->RelationByType(RelationType::LABELLED_BY);
-    Accessible* tempAcc = rel.Next();
-    return tempAcc ? GetNativeFromGeckoAccessible(tempAcc) : nil;
+    if (accWrap) {
+      Relation rel = accWrap->RelationByType(RelationType::LABELLED_BY);
+      Accessible* tempAcc = rel.Next();
+      return tempAcc ? GetNativeFromGeckoAccessible(tempAcc) : nil;
+    }
+    nsTArray<ProxyAccessible*> rel = proxy->RelationByType(RelationType::LABELLED_BY);
+    ProxyAccessible* tempProxy = rel.SafeElementAt(0);
+    return tempProxy ? GetNativeFromProxy(tempProxy) : nil;
   }
   if ([attribute isEqualToString:NSAccessibilityHelpAttribute])
     return [self help];
 
-  if (accWrap->IsTable()) {
-    TableAccessible* table = accWrap->AsTable();
-    if ([attribute isEqualToString:NSAccessibilityRowCountAttribute])
-      return @(table->RowCount());
-    if ([attribute isEqualToString:NSAccessibilityColumnCountAttribute])
-      return @(table->ColCount());
-    if ([attribute isEqualToString:NSAccessibilityRowsAttribute]) {
-      // Create a new array with the list of table rows.
-      NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
-      uint32_t totalCount = accWrap->ChildCount();
-      for (uint32_t i = 0; i < totalCount; i++) {
-        if (accWrap->GetChildAt(i)->IsTableRow()) {
-          mozAccessible* curNative =
-            GetNativeFromGeckoAccessible(accWrap->GetChildAt(i));
-          if (curNative)
-            [nativeArray addObject:GetObjectOrRepresentedView(curNative)];
+  if (accWrap) {
+    if (accWrap->IsTable()) {
+      TableAccessible* table = accWrap->AsTable();
+      if ([attribute isEqualToString:NSAccessibilityRowCountAttribute])
+        return @(table->RowCount());
+      if ([attribute isEqualToString:NSAccessibilityColumnCountAttribute])
+        return @(table->ColCount());
+      if ([attribute isEqualToString:NSAccessibilityRowsAttribute]) {
+        // Create a new array with the list of table rows.
+        NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
+        uint32_t totalCount = accWrap->ChildCount();
+        for (uint32_t i = 0; i < totalCount; i++) {
+          if (accWrap->GetChildAt(i)->IsTableRow()) {
+            mozAccessible* curNative =
+              GetNativeFromGeckoAccessible(accWrap->GetChildAt(i));
+            if (curNative)
+              [nativeArray addObject:GetObjectOrRepresentedView(curNative)];
+          }
         }
+        return nativeArray;
       }
-      return nativeArray;
+    } else if (accWrap->IsTableRow()) {
+      if ([attribute isEqualToString:NSAccessibilityIndexAttribute]) {
+        // Count the number of rows before that one to obtain the row index.
+        uint32_t index = 0;
+        for (int32_t i = accWrap->IndexInParent() - 1; i >= 0; i--) {
+          if (accWrap->GetChildAt(i)->IsTableRow()) {
+            index++;
+          }
+        }
+        return [NSNumber numberWithUnsignedInteger:index];
+      }
+    } else if (accWrap->IsTableCell()) {
+      TableCellAccessible* cell = accWrap->AsTableCell();
+      if ([attribute isEqualToString:NSAccessibilityRowIndexRangeAttribute])
+        return [NSValue valueWithRange:NSMakeRange(cell->RowIdx(),
+                                                   cell->RowExtent())];
+      if ([attribute isEqualToString:NSAccessibilityColumnIndexRangeAttribute])
+        return [NSValue valueWithRange:NSMakeRange(cell->ColIdx(),
+                                                   cell->ColExtent())];
+      if ([attribute isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) {
+        nsAutoTArray<Accessible*, 10> headerCells;
+        cell->RowHeaderCells(&headerCells);
+        return ConvertToNSArray(headerCells);
+      }
+      if ([attribute isEqualToString:NSAccessibilityColumnHeaderUIElementsAttribute]) {
+        nsAutoTArray<Accessible*, 10> headerCells;
+        cell->ColHeaderCells(&headerCells);
+        return ConvertToNSArray(headerCells);
+      }
     }
-  } else if (accWrap->IsTableRow()) {
-    if ([attribute isEqualToString:NSAccessibilityIndexAttribute]) {
-      // Count the number of rows before that one to obtain the row index.
-      uint32_t index = 0;
-      for (int32_t i = accWrap->IndexInParent() - 1; i >= 0; i--) {
-        if (accWrap->GetChildAt(i)->IsTableRow()) {
-          index++;
+  } else if (proxy) {
+    if (proxy->IsTable()) {
+      if ([attribute isEqualToString:NSAccessibilityRowCountAttribute])
+        return @(proxy->TableRowCount());
+      if ([attribute isEqualToString:NSAccessibilityColumnCountAttribute])
+        return @(proxy->TableColumnCount());
+      if ([attribute isEqualToString:NSAccessibilityRowsAttribute]) {
+        // Create a new array with the list of table rows.
+        NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
+        uint32_t totalCount = proxy->ChildrenCount();
+        for (uint32_t i = 0; i < totalCount; i++) {
+          if (proxy->ChildAt(i)->IsTableRow()) {
+            mozAccessible* curNative =
+              GetNativeFromProxy(proxy->ChildAt(i));
+            if (curNative)
+              [nativeArray addObject:GetObjectOrRepresentedView(curNative)];
+          }
         }
+        return nativeArray;
       }
-      return [NSNumber numberWithUnsignedInteger:index];
-    }
-  } else if (accWrap->IsTableCell()) {
-    TableCellAccessible* cell = accWrap->AsTableCell();
-    if ([attribute isEqualToString:NSAccessibilityRowIndexRangeAttribute])
-      return [NSValue valueWithRange:NSMakeRange(cell->RowIdx(),
-                                                 cell->RowExtent())];
-    if ([attribute isEqualToString:NSAccessibilityColumnIndexRangeAttribute])
-      return [NSValue valueWithRange:NSMakeRange(cell->ColIdx(),
-                                                 cell->ColExtent())];
-    if ([attribute isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) {
-      nsAutoTArray<Accessible*, 10> headerCells;
-      cell->RowHeaderCells(&headerCells);
-      return ConvertToNSArray(headerCells);
-    }
-    if ([attribute isEqualToString:NSAccessibilityColumnHeaderUIElementsAttribute]) {
-      nsAutoTArray<Accessible*, 10> headerCells;
-      cell->ColHeaderCells(&headerCells);
-      return ConvertToNSArray(headerCells);
+    } else if (proxy->IsTableRow()) {
+      if ([attribute isEqualToString:NSAccessibilityIndexAttribute]) {
+        // Count the number of rows before that one to obtain the row index.
+        uint32_t index = 0;
+        for (int32_t i = proxy->IndexInParent() - 1; i >= 0; i--) {
+          if (proxy->ChildAt(i)->IsTableRow()) {
+            index++;
+          }
+        }
+        return [NSNumber numberWithUnsignedInteger:index];
+      }
+    } else if (proxy->IsTableCell()) {
+      if ([attribute isEqualToString:NSAccessibilityRowIndexRangeAttribute])
+        return [NSValue valueWithRange:NSMakeRange(proxy->RowIdx(),
+                                                   proxy->RowExtent())];
+      if ([attribute isEqualToString:NSAccessibilityColumnIndexRangeAttribute])
+        return [NSValue valueWithRange:NSMakeRange(proxy->ColIdx(),
+                                                   proxy->ColExtent())];
+      if ([attribute isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) {
+        nsTArray<ProxyAccessible*> headerCells;
+        proxy->RowHeaderCells(&headerCells);
+        return ConvertToNSArray(headerCells);
+      }
+      if ([attribute isEqualToString:NSAccessibilityColumnHeaderUIElementsAttribute]) {
+        nsTArray<ProxyAccessible*> headerCells;
+        proxy->ColHeaderCells(&headerCells);
+        return ConvertToNSArray(headerCells);
+      }
     }
   }
 
   switch (mRole) {
   case roles::MATHML_ROOT:
     if ([attribute isEqualToString:NSAccessibilityMathRootRadicandAttribute])
       return [self childAt:0];
     if ([attribute isEqualToString:NSAccessibilityMathRootIndexAttribute])
@@ -485,19 +548,30 @@ ConvertToNSArray(nsTArray<ProxyAccessibl
       // WebKit sets line thickness to some logical value parsed in the
       // renderer object of the <mfrac> element. It's not clear whether the
       // exact value is relevant to assistive technologies. From a semantic
       // point of view, the only important point is to distinguish between
       // <mfrac> elements that have a fraction bar and those that do not.
       // Per the MathML 3 spec, the latter happens iff the linethickness
       // attribute is of the form [zero-float][optional-unit]. In that case we
       // set line thickness to zero and in the other cases we set it to one.
-      nsCOMPtr<nsIPersistentProperties> attributes = accWrap->Attributes();
       nsAutoString thickness;
-      nsAccUtils::GetAccAttr(attributes, nsGkAtoms::linethickness_, thickness);
+      if (accWrap) {
+        nsCOMPtr<nsIPersistentProperties> attributes = accWrap->Attributes();
+        nsAccUtils::GetAccAttr(attributes, nsGkAtoms::linethickness_, thickness);
+      } else {
+        nsAutoTArray<Attribute, 10> attrs;
+        proxy->Attributes(&attrs);
+        for (size_t i = 0 ; i < attrs.Length() ; i++) {
+          if (attrs.ElementAt(i).Name() == "thickness") {
+            thickness = attrs.ElementAt(i).Value();
+            break;
+          }
+        }
+      }
       double value = 1.0;
       if (!thickness.IsEmpty())
         value = PR_strtod(NS_LossyConvertUTF16toASCII(thickness).get(),
                           nullptr);
       return [NSNumber numberWithInteger:(value ? 1 : 0)];
     }
     break;
   case roles::MATHML_SUB:
@@ -601,37 +675,44 @@ ConvertToNSArray(nsTArray<ProxyAccessibl
     [self focus];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (id)accessibilityHitTest:(NSPoint)point
 {
   AccessibleWrap* accWrap = [self getGeckoAccessible];
-  if (!accWrap)
+  ProxyAccessible* proxy = [self getProxyAccessible];
+  if (!accWrap && !proxy)
     return nil;
 
   // Convert the given screen-global point in the cocoa coordinate system (with
   // origin in the bottom-left corner of the screen) into point in the Gecko
   // coordinate system (with origin in a top-left screen point).
   NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
   NSPoint tmpPoint = NSMakePoint(point.x,
                                  [mainView frame].size.height - point.y);
   nsIntPoint geckoPoint = nsCocoaUtils::
     CocoaPointsToDevPixels(tmpPoint, nsCocoaUtils::GetBackingScaleFactor(mainView));
 
-  Accessible* child =
-    accWrap->ChildAtPoint(geckoPoint.x, geckoPoint.y,
-                          Accessible::eDeepestChild);
+  mozAccessible* nativeChild = nil;
+  if (accWrap) {
+    Accessible* child = accWrap->ChildAtPoint(geckoPoint.x, geckoPoint.y,
+                                  Accessible::eDeepestChild);
+    if (child)
+      nativeChild = GetNativeFromGeckoAccessible(child);
+  } else if (proxy) {
+    ProxyAccessible* child = proxy->ChildAtPoint(geckoPoint.x, geckoPoint.y,
+                                  Accessible::eDeepestChild);
+    if (child)
+      nativeChild = GetNativeFromProxy(child);
+  }
 
-  if (child) {
-    mozAccessible* nativeChild = GetNativeFromGeckoAccessible(child);
-    if (nativeChild)
-      return GetClosestInterestingAccessible(nativeChild);
-  }
+  if (nativeChild)
+    return GetClosestInterestingAccessible(nativeChild);
 
   // if we didn't find anything, return ourself (or the first unignored ancestor).
   return GetClosestInterestingAccessible(self);
 }
 
 - (NSArray*)accessibilityActionNames
 {
   return nil;
@@ -646,26 +727,34 @@ ConvertToNSArray(nsTArray<ProxyAccessibl
 
 - (void)accessibilityPerformAction:(NSString*)action
 {
 }
 
 - (id)accessibilityFocusedUIElement
 {
   AccessibleWrap* accWrap = [self getGeckoAccessible];
-  if (!accWrap)
+  ProxyAccessible* proxy = [self getProxyAccessible];
+  if (!accWrap && !proxy)
     return nil;
 
-  Accessible* focusedGeckoChild = accWrap->FocusedChild();
-  if (focusedGeckoChild) {
-    mozAccessible *focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild);
-    if (focusedChild)
-      return GetClosestInterestingAccessible(focusedChild);
+  mozAccessible* focusedChild = nil;
+  if (accWrap) {
+    Accessible* focusedGeckoChild = accWrap->FocusedChild();
+    if (focusedGeckoChild)
+      focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild);
+  } else if (proxy) {
+    ProxyAccessible* focusedGeckoChild = proxy->FocusedChild();
+    if (focusedGeckoChild)
+      focusedChild = GetNativeFromProxy(focusedGeckoChild);
   }
 
+  if (focusedChild)
+    return GetClosestInterestingAccessible(focusedChild);
+
   // return ourself if we can't get a native focused child.
   return GetClosestInterestingAccessible(self);
 }
 
 #pragma mark -
 
 - (id <mozAccessible>)parent
 {
--- a/accessible/mac/mozActionElements.mm
+++ b/accessible/mac/mozActionElements.mm
@@ -73,17 +73,17 @@ enum CheckboxValue {
 
   return [super accessibilityAttributeValue:attribute];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (BOOL)accessibilityIsIgnored
 {
-  return ![self getGeckoAccessible];
+  return ![self getGeckoAccessible] && ![self getProxyAccessible];
 }
 
 - (NSArray*)accessibilityActionNames
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   if ([self isEnabled]) {
     if ([self hasPopup])
--- a/accessible/mac/mozTextAccessible.mm
+++ b/accessible/mac/mozTextAccessible.mm
@@ -50,17 +50,17 @@ ToNSString(id aValue)
 - (NSString*)text;
 - (NSString*)stringFromRange:(NSRange*)range;
 @end
 
 @implementation mozTextAccessible
 
 - (BOOL)accessibilityIsIgnored
 {
-  return ![self getGeckoAccessible];
+  return ![self getGeckoAccessible] && ![self getProxyAccessible];
 }
 
 - (NSArray*)accessibilityAttributeNames
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   static NSMutableArray* supportedAttributes = nil;
   if (!supportedAttributes) {
--- a/accessible/tests/mochitest/attributes/test_xml-roles.html
+++ b/accessible/tests/mochitest/attributes/test_xml-roles.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html>
 <head>
-  <title>XML landmark tests</title>
+  <title>XML roles tests</title>
   <link rel="stylesheet" type="text/css"
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
@@ -31,16 +31,70 @@
       testAttrs("section", {"xml-roles" : "region"}, true);
       testAttrs("main", {"xml-roles" : "main"}, true); // // ARIA override
       testAttrs("form", {"xml-roles" : "form"}, true);
       testAttrs("article", {"xml-roles" : "article"}, true);
       testAttrs("main_element", {"xml-roles" : "main"}, true);
 
       testAttrs("search", {"xml-roles" : "searchbox"}, true);
 
+      testAttrs("open-1", {"xml-roles" : "open-fence"}, true);
+      testAttrs("open-2", {"xml-roles" : "open-fence"}, true);
+      testAttrs("open-3", {"xml-roles" : "open-fence"}, true);
+      testAttrs("open-4", {"xml-roles" : "open-fence"}, true);
+      testAttrs("open-5", {"xml-roles" : "open-fence"}, true);
+      testAttrs("open-6", {"xml-roles" : "open-fence"}, true);
+      testAttrs("open-7", {"xml-roles" : "open-fence"}, true);
+
+      testAttrs("sep-1", {"xml-roles" : "separator"}, true);
+      testAttrs("sep-2", {"xml-roles" : "separator"}, true);
+      testAttrs("sep-3", {"xml-roles" : "separator"}, true);
+      testAttrs("sep-4", {"xml-roles" : "separator"}, true);
+      testAttrs("sep-5", {"xml-roles" : "separator"}, true);
+      testAttrs("sep-6", {"xml-roles" : "separator"}, true);
+      testAttrs("sep-7", {"xml-roles" : "separator"}, true);
+
+      testAttrs("close-1", {"xml-roles" : "close-fence"}, true);
+      testAttrs("close-2", {"xml-roles" : "close-fence"}, true);
+      testAttrs("close-3", {"xml-roles" : "close-fence"}, true);
+      testAttrs("close-4", {"xml-roles" : "close-fence"}, true);
+      testAttrs("close-5", {"xml-roles" : "close-fence"}, true);
+      testAttrs("close-6", {"xml-roles" : "close-fence"}, true);
+      testAttrs("close-7", {"xml-roles" : "close-fence"}, true);
+
+      testAttrs("num", {"xml-roles" : "numerator"}, true);
+      testAttrs("den", {"xml-roles" : "denominator"}, true);
+
+      testAttrs("sub-1", {"xml-roles" : "subscript"}, true);
+      testAttrs("sub-2", {"xml-roles" : "subscript"}, true);
+      testAttrs("sub-3", {"xml-roles" : "subscript"}, true);
+      testAttrs("sup-1", {"xml-roles" : "superscript"}, true);
+      testAttrs("sup-2", {"xml-roles" : "superscript"}, true);
+      testAttrs("sup-3", {"xml-roles" : "superscript"}, true);
+      testAttrs("sup-4", {"xml-roles" : "superscript"}, true);
+      testAttrs("presub-1", {"xml-roles" : "presubscript"}, true);
+      testAttrs("presub-2", {"xml-roles" : "presubscript"}, true);
+      testAttrs("presup-1", {"xml-roles" : "presuperscript"}, true);
+
+      testAttrs("under-1", {"xml-roles" : "underscript"}, true);
+      testAttrs("under-2", {"xml-roles" : "underscript"}, true);
+      testAttrs("over-1", {"xml-roles" : "overscript"}, true);
+      testAttrs("over-2", {"xml-roles" : "overscript"}, true);
+
+      testAttrs("root-index-1", {"xml-roles" : "root-index"}, true);
+
+      testAttrs("base-1", {"xml-roles" : "base"}, true);
+      testAttrs("base-2", {"xml-roles" : "base"}, true);
+      testAttrs("base-3", {"xml-roles" : "base"}, true);
+      testAttrs("base-4", {"xml-roles" : "base"}, true);
+      testAttrs("base-5", {"xml-roles" : "base"}, true);
+      testAttrs("base-6", {"xml-roles" : "base"}, true);
+      testAttrs("base-7", {"xml-roles" : "base"}, true);
+      testAttrs("base-8", {"xml-roles" : "base"}, true);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
@@ -104,10 +158,92 @@
   <aside id="aside">by the way I am an aside</aside>
   <section id="section">a section</section>
   <article id="main" role="main">a main area</article>
   <article id="form" role="form">a form area</article>
   <article id="article">article</article>
   <main id="main_element">another main area</main>
 
   <input id="search" type="search"/>
+
+  <!-- open-fence, separator, close-fence -->
+  <math><mo id="open-1">(</mo><mi>x</mi><mo id="sep-1">,</mo><mi>y</mi><mo id="close-1">)</mo></math>
+  <math><mrow><mo id="open-2">(</mo><mi>x</mi><mo id="sep-2">,</mo><mi>y</mi><mo id="close-2">)</mo></mrow></math>
+  <math><mstyle><mo id="open-3">(</mo><mi>x</mi><mo id="sep-3">,</mo><mi>y</mi><mo id="close-3">)</mo></mstyle></math>
+  <math><msqrt><mo id="open-4">(</mo><mi>x</mi><mo id="sep-4">,</mo><mi>y</mi><mo id="close-4">)</mo></msqrt></math>
+  <math><menclose><mo id="open-5">(</mo><mi>x</mi><mo id="sep-5">,</mo><mi>y</mi><mo id="close-5">)</mo></menclose></math>
+  <math><merror><mo id="open-6">(</mo><mi>x</mi><mo id="sep-6">,</mo><mi>y</mi><mo id="close-6">)</mo></merror></math>
+  <math><mtable><mtr><mtd><mo id="open-7">(</mo><mi>x</mi><mo id="sep-7">,</mo><mi>y</mi><mo id="close-7">)</mo></mtd></mtr></mtable></math>
+
+  <!-- numerator, denominator -->
+  <math>
+    <mfrac>
+      <mi id="num">a</mi>
+      <mi id="den">b</mi>
+    </mfrac>
+  </math>
+
+  <!-- subscript, superscript, presubscript, presuperscript -->
+  <math>
+    <msub>
+      <mi id="base-1">a</mi>
+      <mi id="sub-1">b</mi>
+    </msub>
+  </math>
+  <math>
+    <msup>
+      <mi id="base-2">a</mi>
+      <mi id="sup-1">b</mi>
+    </msup>
+  </math>
+  <math>
+    <msubsup>
+      <mi id="base-3">a</mi>
+      <mi id="sub-2">b</mi>
+      <mi id="sup-2">c</mi>
+    </msubsup>
+  </math>
+  <math>
+    <mmultiscripts>
+      <mi id="base-4">a</mi>
+      <mi id="sub-3">b</mi>
+      <mi id="sup-3">c</mi>
+      <none/>
+      <mi id="sup-4">d</mi>
+      <mprescripts/>
+      <mi id="presub-1">e</mi>
+      <none/>
+      <mi id="presub-2">f</mi>
+      <mi id="presup-1">g</mi>
+    </mmultiscripts>
+  </math>
+
+  <!-- underscript, overscript -->
+  <math>
+    <munder>
+      <mi id="base-5">a</mi>
+      <mi id="under-1">b</mi>
+    </munder>
+  </math>
+  <math>
+    <mover>
+      <mi id="base-6">a</mi>
+      <mi id="over-1">b</mi>
+    </mover>
+  </math>
+  <math>
+    <munderover>
+      <mi id="base-7">a</mi>
+      <mi id="under-2">b</mi>
+      <mi id="over-2">c</mi>
+    </munderover>
+  </math>
+
+  <!-- root-index -->
+  <math>
+    <mroot>
+      <mi id="base-8">a</mi>
+      <mi id="root-index-1">b</mi>
+    </mroot>
+  </math>
+
 </body>
 </html>
--- a/b2g/chrome/content/desktop.js
+++ b/b2g/chrome/content/desktop.js
@@ -5,23 +5,19 @@
 let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
 let isMulet = "ResponsiveUI" in browserWindow;
 
 // Enable touch event shim on desktop that translates mouse events
 // into touch ones
 function enableTouch() {
   let require = Cu.import('resource://gre/modules/devtools/Loader.jsm', {})
                   .devtools.require;
-  let { TouchEventHandler } = require('devtools/touch-events');
-  let chromeEventHandler = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIWebNavigation)
-                                 .QueryInterface(Ci.nsIDocShell)
-                                 .chromeEventHandler || window;
-  let touchEventHandler = new TouchEventHandler(chromeEventHandler);
-  touchEventHandler.start();
+  let { TouchEventSimulator } = require('devtools/toolkit/touch/simulator');
+  let touchEventSimulator = new TouchEventSimulator(shell.contentBrowser);
+  touchEventSimulator.start();
 }
 
 function setupButtons() {
   let homeButton = document.getElementById('home-button');
   if (!homeButton) {
     // The toolbar only exists in b2g desktop build with
     // FXOS_SIMULATOR turned on.
     return;
--- 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="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3fac3ed7b8c887351098ffc677769ddc36abb3d0"/>
   <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="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f48264d4da9680b6e02c9d0459ac273308e21daf"/>
   <!-- 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"/>
@@ -105,17 +105,17 @@
   <project name="platform/libcore" path="libcore" revision="baf7d8068dd501cfa338d3a8b1b87216d6ce0571"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="50c4430e32849530ced32680fd6ee98963b3f7ac"/>
   <project name="platform/ndk" path="ndk" revision="e58ef003be4306bb53a8c11331146f39e4eab31f"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="23404f05422c6bf3c39573325a4f4909167671b4"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="c792f0bd9fff7aea2887c60bbb3a9bbdb534ffa3"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="6a1bb59af65b6485b1090522f66fac95c3f9e22c"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="69d524e80cdf3981006627c65ac85f3a871238a3"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="3f6ccfc6bc73e28765d70bbe5c277a6276472b08"/>
   <project name="platform/system/extras" path="system/extras" revision="576f57b6510de59c08568b53c0fb60588be8689e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
   <project name="platform/system/netd" path="system/netd" revision="a6531f7befb49b1c81bc0de7e51c5482b308e1c5"/>
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
   <project name="platform/system/vold" path="system/vold" revision="42fa2a0f14f965970a4b629a176bbd2666edf017"/>
   <project name="platform/external/curl" path="external/curl" revision="e68addd988448959ea8157c5de637346b4180c33"/>
   <project name="platform/external/icu4c" path="external/icu4c" revision="d3ec7428eb276db43b7ed0544e09344a6014806c"/>
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="76c4bf4bc430a1b8317f2f21ef735867733e50cc"/>
--- 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="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3fac3ed7b8c887351098ffc677769ddc36abb3d0"/>
   <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="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f48264d4da9680b6e02c9d0459ac273308e21daf"/>
   <!-- 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"/>
@@ -110,17 +110,17 @@
   <project name="platform/libcore" path="libcore" revision="e195beab082c09217318fc19250caeaf4c1bd800"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="feeb36c2bd4adfe285f98f5de92e0f3771b2c115"/>
   <project name="platform/ndk" path="ndk" revision="e58ef003be4306bb53a8c11331146f39e4eab31f"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="23404f05422c6bf3c39573325a4f4909167671b4"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="c792f0bd9fff7aea2887c60bbb3a9bbdb534ffa3"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="6a1bb59af65b6485b1090522f66fac95c3f9e22c"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="cfcef469537869947abb9aa1d656774cc2678d4c"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="3f6ccfc6bc73e28765d70bbe5c277a6276472b08"/>
   <project name="platform/system/extras" path="system/extras" revision="10e78a05252b3de785f88c2d0b9ea8a428009c50"/>
   <project name="platform/system/media" path="system/media" revision="188b3e51e0a2ce1e16dc8067edef7be3d2365ad9"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
   <project name="platform/system/netd" path="system/netd" revision="3ae56364946d4a5bf5a5f83f12f9a45a30398e33"/>
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
   <project name="platform/system/vold" path="system/vold" revision="fe12a9e2268da653d1cd4c39ec89a42211d22f25"/>
   <!--original fetch url was http://sprdsource.spreadtrum.com:8085/b2g/android-->
   <remote fetch="https://git.mozilla.org/external/sprd-aosp" name="sprd-aosp"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--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="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3fac3ed7b8c887351098ffc677769ddc36abb3d0"/>
   <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="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"/>
--- 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="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3fac3ed7b8c887351098ffc677769ddc36abb3d0"/>
   <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="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f48264d4da9680b6e02c9d0459ac273308e21daf"/>
   <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"/>
@@ -112,17 +112,17 @@
   <project name="platform/libnativehelper" path="libnativehelper" revision="4792069e90385889b0638e97ae62c67cdf274e22"/>
   <project name="platform/ndk" path="ndk" revision="7666b97bbaf1d645cdd6b4430a367b7a2bb53369"/>
   <project name="platform/prebuilts/misc" path="prebuilts/misc" revision="f6ab40b3257abc07741188fd173ac392575cc8d2"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="e52099755d0bd3a579130eefe8e58066cc6c0cb6"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="02c32feb2fe97037be0ac4dace3a6a5025ac895d"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="3f6ccfc6bc73e28765d70bbe5c277a6276472b08"/>
   <project name="platform_system_core" path="system/core" remote="b2g" revision="542d1f59dc331b472307e5bd043101d14d5a3a3e"/>
   <project name="platform/system/extras" path="system/extras" revision="18c1180e848e7ab8691940481f5c1c8d22c37b3e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
   <project name="platform/system/media" path="system/media" revision="d90b836f66bf1d9627886c96f3a2d9c3007fbb80"/>
   <project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
--- 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="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3fac3ed7b8c887351098ffc677769ddc36abb3d0"/>
   <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="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f48264d4da9680b6e02c9d0459ac273308e21daf"/>
   <!-- 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"/>
@@ -110,17 +110,17 @@
   <project name="platform/libcore" path="libcore" revision="9877ade9617bb0db6e59aa2a54719a9bc92600f3"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="46c96ace65eb1ccab05bf15b9bf8e53e443039af"/>
   <project name="platform/ndk" path="ndk" revision="cb5519af32ae7b4a9c334913a612462ecd04c5d0"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="23404f05422c6bf3c39573325a4f4909167671b4"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="6aa61f8557a22039a30b42b7f283996381fd625d"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="6a1bb59af65b6485b1090522f66fac95c3f9e22c"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="b562b01c93de9578d5db537b6a602a38e1aaa0ce"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="387f03e815f57d536dd922706db1622bddba8d81"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="3f6ccfc6bc73e28765d70bbe5c277a6276472b08"/>
   <project name="platform/system/extras" path="system/extras" revision="5356165f67f4a81c2ef28671c13697f1657590df"/>
   <project name="platform/system/media" path="system/media" revision="be0e2fe59a8043fa5200f75697df9220a99abe9d"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
   <project name="platform/system/netd" path="system/netd" revision="36704b0da24debcab8090156568ac236315036bb"/>
   <project name="platform/system/security" path="system/security" revision="583374f69f531ba68fc3dcbff1f74893d2a96406"/>
   <project name="platform/system/vold" path="system/vold" revision="d4455b8cf361f8353e8aebac15ffd64b4aedd2b9"/>
   <project name="platform/external/icu4c" path="external/icu4c" remote="aosp" revision="b4c6379528887dc25ca9991a535a8d92a61ad6b6"/>
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="f3cedd7fd9b1649aa5107d466be9078bb7602af6"/>
--- 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="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3fac3ed7b8c887351098ffc677769ddc36abb3d0"/>
   <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="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f48264d4da9680b6e02c9d0459ac273308e21daf"/>
   <!-- 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"/>
@@ -122,17 +122,17 @@
   <project name="platform/libcore" path="libcore" revision="bdec7d684c083760bef7bc4ba2429cceccaaf7d0"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="27bcc086236cedd31c056303e255c6d0ea3d4a50"/>
   <project name="platform/ndk" path="ndk" revision="42e85f81cc6c74af145056ee80b06e520cccb9a7"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="f105a2d852c988fb1aa16a1e758ca7f93dd54fe9"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="1d080491f26dfdfd76d5bbc3e6b40c660e8565af"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="2c0d193349c55337e37196a7f2d5cef37753ed3e"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="61a10cbd19d6b7fc052a8cb92dfa1b37b93754f3"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="9e892a67a01671f312c76b0880dedaa6ba478148"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="3f6ccfc6bc73e28765d70bbe5c277a6276472b08"/>
   <project name="platform/system/extras" path="system/extras" revision="47fa016e2248b80aebd5928402c7409f8e0ca64e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
   <project name="platform/system/media" path="system/media" revision="70bfebc66d9c6a4c614a8c7efde90e8e7e1d8641"/>
   <project name="platform/system/netd" path="system/netd" revision="d113f0ceefa9ce29eb3c86e2d23c7417a70b4048"/>
   <project name="platform/system/security" path="system/security" revision="94e1617f6f2bc2286d005e79cffa6bf0721b06b3"/>
   <project name="platform/system/vold" path="system/vold" revision="c065e301e38ea0c241164e2a373e1ecefbeaf2ec"/>
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="e372b6a77f71c8f9fbbf6f8adbfa7bf8ef45dc60"/>
   <project name="platform_frameworks_base" path="frameworks/base" remote="b2g" revision="04e26ebdc36ca83f4ee3e9e2082b3fcf04c5b971"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--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="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3fac3ed7b8c887351098ffc677769ddc36abb3d0"/>
   <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="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"/>
--- 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="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3fac3ed7b8c887351098ffc677769ddc36abb3d0"/>
   <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="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f48264d4da9680b6e02c9d0459ac273308e21daf"/>
   <!-- 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"/>
@@ -104,17 +104,17 @@
   <project name="platform/libcore" path="libcore" revision="baf7d8068dd501cfa338d3a8b1b87216d6ce0571"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="50c4430e32849530ced32680fd6ee98963b3f7ac"/>
   <project name="platform/ndk" path="ndk" revision="e58ef003be4306bb53a8c11331146f39e4eab31f"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="23404f05422c6bf3c39573325a4f4909167671b4"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="c792f0bd9fff7aea2887c60bbb3a9bbdb534ffa3"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="6a1bb59af65b6485b1090522f66fac95c3f9e22c"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="69d524e80cdf3981006627c65ac85f3a871238a3"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="3f6ccfc6bc73e28765d70bbe5c277a6276472b08"/>
   <project name="platform/system/extras" path="system/extras" revision="576f57b6510de59c08568b53c0fb60588be8689e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
   <project name="platform/system/netd" path="system/netd" revision="a6531f7befb49b1c81bc0de7e51c5482b308e1c5"/>
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
   <project name="platform/system/vold" path="system/vold" revision="42fa2a0f14f965970a4b629a176bbd2666edf017"/>
   <project name="platform/external/curl" path="external/curl" revision="e68addd988448959ea8157c5de637346b4180c33"/>
   <project name="platform/external/icu4c" path="external/icu4c" revision="d3ec7428eb276db43b7ed0544e09344a6014806c"/>
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="76c4bf4bc430a1b8317f2f21ef735867733e50cc"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36", 
+        "git_revision": "3fac3ed7b8c887351098ffc677769ddc36abb3d0", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "dfd4a9e8f31e64d427030d3612a48f7dbcada5d3", 
+    "revision": "1a91ed6e76994d6279bd2cf81ba1416e86aa2014", 
     "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="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3fac3ed7b8c887351098ffc677769ddc36abb3d0"/>
   <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="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f48264d4da9680b6e02c9d0459ac273308e21daf"/>
   <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"/>
@@ -112,17 +112,17 @@
   <project name="platform/libnativehelper" path="libnativehelper" revision="4792069e90385889b0638e97ae62c67cdf274e22"/>
   <project name="platform/ndk" path="ndk" revision="7666b97bbaf1d645cdd6b4430a367b7a2bb53369"/>
   <project name="platform/prebuilts/misc" path="prebuilts/misc" revision="f6ab40b3257abc07741188fd173ac392575cc8d2"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="e52099755d0bd3a579130eefe8e58066cc6c0cb6"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="02c32feb2fe97037be0ac4dace3a6a5025ac895d"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="3f6ccfc6bc73e28765d70bbe5c277a6276472b08"/>
   <project name="platform_system_core" path="system/core" remote="b2g" revision="542d1f59dc331b472307e5bd043101d14d5a3a3e"/>
   <project name="platform/system/extras" path="system/extras" revision="18c1180e848e7ab8691940481f5c1c8d22c37b3e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
   <project name="platform/system/media" path="system/media" revision="d90b836f66bf1d9627886c96f3a2d9c3007fbb80"/>
   <project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
--- 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="77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3fac3ed7b8c887351098ffc677769ddc36abb3d0"/>
   <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="f2a1305448efefacb2ec7e654d797bb9fbe7202e"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f48264d4da9680b6e02c9d0459ac273308e21daf"/>
   <!-- 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"/>
@@ -122,17 +122,17 @@
   <project name="platform/libcore" path="libcore" revision="bdec7d684c083760bef7bc4ba2429cceccaaf7d0"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="27bcc086236cedd31c056303e255c6d0ea3d4a50"/>
   <project name="platform/ndk" path="ndk" revision="42e85f81cc6c74af145056ee80b06e520cccb9a7"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="f105a2d852c988fb1aa16a1e758ca7f93dd54fe9"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="1d080491f26dfdfd76d5bbc3e6b40c660e8565af"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="2c0d193349c55337e37196a7f2d5cef37753ed3e"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="61a10cbd19d6b7fc052a8cb92dfa1b37b93754f3"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="9e892a67a01671f312c76b0880dedaa6ba478148"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="3f6ccfc6bc73e28765d70bbe5c277a6276472b08"/>
   <project name="platform/system/extras" path="system/extras" revision="47fa016e2248b80aebd5928402c7409f8e0ca64e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
   <project name="platform/system/media" path="system/media" revision="70bfebc66d9c6a4c614a8c7efde90e8e7e1d8641"/>
   <project name="platform/system/netd" path="system/netd" revision="d113f0ceefa9ce29eb3c86e2d23c7417a70b4048"/>
   <project name="platform/system/security" path="system/security" revision="94e1617f6f2bc2286d005e79cffa6bf0721b06b3"/>
   <project name="platform/system/vold" path="system/vold" revision="c065e301e38ea0c241164e2a373e1ecefbeaf2ec"/>
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="e372b6a77f71c8f9fbbf6f8adbfa7bf8ef45dc60"/>
   <project name="platform_frameworks_base" path="frameworks/base" remote="b2g" revision="04e26ebdc36ca83f4ee3e9e2082b3fcf04c5b971"/>
--- a/b2g/installer/Makefile.in
+++ b/b2g/installer/Makefile.in
@@ -3,17 +3,23 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 STANDALONE_MAKEFILE := 1
 
 include $(topsrcdir)/config/rules.mk
 
 MOZ_PKG_REMOVALS = $(srcdir)/removed-files.in
 
-MOZ_PKG_MANIFEST_P = $(srcdir)/package-manifest.in
+MOZ_PKG_MANIFEST = $(srcdir)/package-manifest.in
+
+ifdef MOZ_CHROME_MULTILOCALE
+MOZ_PKG_MANIFEST_DEPS = locale-manifest.in
+
+DEFINES += -DPKG_LOCALE_MANIFEST=$(CURDIR)/locale-manifest.in
+endif
 
 DEFINES += \
 	-DMOZ_APP_NAME=$(MOZ_APP_NAME) \
 	-DPREF_DIR=$(PREF_DIR) \
 	$(NULL)
 
 DEFINES += -DJAREXT=
 
@@ -37,20 +43,16 @@ endif
 ifdef MOZ_DEBUG
 DEFINES += -DMOZ_DEBUG=1
 endif
 
 ifdef ENABLE_MARIONETTE
 DEFINES += -DENABLE_MARIONETTE=1
 endif
 
-ifdef MOZ_PKG_MANIFEST_P
-MOZ_PKG_MANIFEST = package-manifest
-endif
-
 MOZ_PACKAGER_MINIFY=1
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 ifndef _APPNAME
 _APPNAME = $(MOZ_MACBUNDLE_NAME)
 endif
 ifndef _BINPATH
 _BINPATH = /$(_APPNAME)/Contents/MacOS
@@ -102,29 +104,26 @@ endif
 
 ifdef MOZ_WIDGET_GTK
 DEFINES += -DMOZ_GTK=1
 ifdef MOZ_ENABLE_GTK3
 DEFINES += -DMOZ_GTK3=1
 endif
 endif
 
-ifdef MOZ_PKG_MANIFEST_P
-$(MOZ_PKG_MANIFEST): $(MOZ_PKG_MANIFEST_P) FORCE
-	$(call py_action,preprocessor,$(DEFINES) $(ACDEFINES) $< -o $@)
 ifdef MOZ_CHROME_MULTILOCALE
-	printf '\n[multilocale]\n' >> $@
+locale-manifest.in: $(GLOBAL_DEPS) FORCE
+	printf '\n[multilocale]\n' > $@
 	for LOCALE in $(MOZ_CHROME_MULTILOCALE) ;\
 	do \
 	  printf '$(BINPATH)/chrome/'"$$LOCALE"'$(JAREXT)\n' >> $@; \
 	  printf '$(BINPATH)/chrome/'"$$LOCALE"'.manifest\n' >> $@; \
 	done
-endif
 
-GARBAGE += $(MOZ_PKG_MANIFEST)
+GARBAGE += locale-manifest.in
 endif
 
 ifdef FXOS_SIMULATOR
 export MAKE
 
 .PHONY: simulator
 simulator: make-package
 	@echo 'Building simulator addon...'
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -975,8 +975,12 @@ bin/libfreebl_32int64_3.so
 @RESPATH@/fonts/*
 #endif
 
 ; media
 #ifdef MOZ_EME
 @RESPATH@/gmp-clearkey/0.1/@DLL_PREFIX@clearkey@DLL_SUFFIX@
 @RESPATH@/gmp-clearkey/0.1/clearkey.info
 #endif
+
+#ifdef PKG_LOCALE_MANIFEST
+#include @PKG_LOCALE_MANIFEST@
+#endif
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1436560690000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1437165748000">
   <emItems>
       <emItem  blockID="i58" id="webmaster@buzzzzvideos.info">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i71" id="youtube@2youtube.com">
@@ -360,16 +360,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i52" id="ff-ext@youtube">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i966" id="{5C655500-E712-41e7-9349-CE462F844B19}">
+                        <versionRange  minVersion="0" maxVersion="1.0.1-signed" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i504" id="aytac@abc.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i808" id="{c96d1ae6-c4cf-4984-b110-f5f561b33b5a}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
@@ -3045,39 +3051,75 @@
       <match name="name" exp="Java(\(TM\))? Plug-in 10\.(4[5-9]|(5|6)\d|7[0-8])(\.[0-9]+)?([^\d\._]|$)" />            <match name="filename" exp="libnpjp2\.so" />                      <versionRange  severity="0" vulnerabilitystatus="1"></versionRange>
                             <infoURL>https://java.com/</infoURL>
           </pluginItem>
       <pluginItem  blockID="p912">
       <match name="name" exp="Java(\(TM\))? Plug-in 11\.(\d|[1-3]\d|4[0-4])(\.[0-9]+)?([^\d\._]|$)" />            <match name="filename" exp="libnpjp2\.so" />                      <versionRange  severity="0" vulnerabilitystatus="1"></versionRange>
                             <infoURL>https://java.com/</infoURL>
           </pluginItem>
       <pluginItem  blockID="p928">
-                  <match name="filename" exp="(NPSWF32.*\.dll)|(Flash\ Player\.plugin)" />                      <versionRange  minVersion="13.0.0.269" maxVersion="13.0.0.295" severity="0" vulnerabilitystatus="1"></versionRange>
+                  <match name="filename" exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" />                      <versionRange  minVersion="13.0.0.269" maxVersion="13.0.0.295" severity="0" vulnerabilitystatus="1"></versionRange>
                             <infoURL>https://get.adobe.com/flashplayer/</infoURL>
           </pluginItem>
       <pluginItem  blockID="p930">
-                  <match name="filename" exp="(NPSWF32.*\.dll)|(Flash\ Player\.plugin)" />                      <versionRange  minVersion="16.0.0.305" maxVersion="18.0.0.193" severity="0" vulnerabilitystatus="1"></versionRange>
+                  <match name="filename" exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" />                      <versionRange  minVersion="16.0.0.305" maxVersion="18.0.0.193" severity="0" vulnerabilitystatus="1"></versionRange>
                             <infoURL>https://get.adobe.com/flashplayer/</infoURL>
           </pluginItem>
       <pluginItem  os="Linux" blockID="p932">
                   <match name="filename" exp="libflashplayer\.so" />                      <versionRange  minVersion="11.2.202.442" maxVersion="11.2.202.467" severity="0" vulnerabilitystatus="1"></versionRange>
                             <infoURL>https://get.adobe.com/flashplayer/</infoURL>
           </pluginItem>
       <pluginItem  os="Linux" blockID="p936">
                   <match name="filename" exp="libflashplayer\.so" />                      <versionRange  minVersion="11.2.202.468" maxVersion="11.2.202.480" severity="0" vulnerabilitystatus="1"></versionRange>
                             <infoURL>https://get.adobe.com/flashplayer/</infoURL>
           </pluginItem>
       <pluginItem  blockID="p938">
-                  <match name="filename" exp="(NPSWF32.*\.dll)|(Flash\ Player\.plugin)" />                      <versionRange  minVersion="18.0.0.194" maxVersion="18.0.0.202" severity="0" vulnerabilitystatus="1"></versionRange>
+                  <match name="filename" exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" />                      <versionRange  minVersion="18.0.0.194" maxVersion="18.0.0.202" severity="0" vulnerabilitystatus="1"></versionRange>
                             <infoURL>https://get.adobe.com/flashplayer/</infoURL>
           </pluginItem>
       <pluginItem  blockID="p940">
-                  <match name="filename" exp="(NPSWF32.*\.dll)|(Flash\ Player\.plugin)" />                      <versionRange  minVersion="13.0.0.296" maxVersion="13.0.0.301" severity="0" vulnerabilitystatus="1"></versionRange>
+                  <match name="filename" exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" />                      <versionRange  minVersion="13.0.0.296" maxVersion="13.0.0.301" severity="0" vulnerabilitystatus="1"></versionRange>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
+      <pluginItem  blockID="p944">
+                  <match name="filename" exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" />                      <versionRange  minVersion="13.0.0.302" maxVersion="13.0.0.302" severity="0" vulnerabilitystatus="1"></versionRange>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
+      <pluginItem  blockID="p946">
+                  <match name="filename" exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" />                      <versionRange  minVersion="18.0.0.203" maxVersion="18.0.0.203" severity="0" vulnerabilitystatus="1"></versionRange>
+                            <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+          </pluginItem>
+      <pluginItem  os="Linux" blockID="p948">
+                  <match name="filename" exp="libflashplayer\.so" />                      <versionRange  minVersion="11.2.202.481" maxVersion="11.2.202.481" severity="0" vulnerabilitystatus="1"></versionRange>
                             <infoURL>https://get.adobe.com/flashplayer/</infoURL>
           </pluginItem>
+      <pluginItem  blockID="p954">
+                  <match name="filename" exp="JavaAppletPlugin\.plugin" />                      <versionRange  minVersion="Java 7 Update 79" maxVersion="Java 7 Update 80" severity="0" vulnerabilitystatus="1"></versionRange>
+                            <infoURL>https://java.com/</infoURL>
+          </pluginItem>
+      <pluginItem  blockID="p956">
+                  <match name="filename" exp="JavaAppletPlugin\.plugin" />                      <versionRange  minVersion="Java 8 Update 45" maxVersion="Java 8 Update 45" severity="0" vulnerabilitystatus="1"></versionRange>
+                            <infoURL>https://java.com/</infoURL>
+          </pluginItem>
+      <pluginItem  blockID="p958">
+      <match name="name" exp="Java\(TM\) Platform SE 7 U(79|80)(\s[^\d\._U]|$)" />            <match name="filename" exp="npjp2\.dll" />                      <versionRange  severity="0" vulnerabilitystatus="1"></versionRange>
+                            <infoURL>https://java.com/</infoURL>
+          </pluginItem>
+      <pluginItem  blockID="p960">
+      <match name="name" exp="Java\(TM\) Platform SE 8 U45(\s[^\d\._U]|$)" />            <match name="filename" exp="npjp2\.dll" />                      <versionRange  severity="0" vulnerabilitystatus="1"></versionRange>
+                            <infoURL>https://java.com/</infoURL>
+          </pluginItem>
+      <pluginItem  blockID="p962">
+      <match name="name" exp="Java(\(TM\))? Plug-in 10\.(79|80)(\.[0-9]+)?([^\d\._]|$)" />            <match name="filename" exp="libnpjp2\.so" />                      <versionRange  severity="0" vulnerabilitystatus="1"></versionRange>
+                            <infoURL>https://java.com/</infoURL>
+          </pluginItem>
+      <pluginItem  blockID="p964">
+      <match name="name" exp="Java(\(TM\))? Plug-in 11\.45(\.[0-9]+)?([^\d\._]|$)" />            <match name="filename" exp="libnpjp2\.so" />                      <versionRange  severity="0" vulnerabilitystatus="1"></versionRange>
+                            <infoURL>https://java.com/</infoURL>
+          </pluginItem>
     </pluginItems>
 
   <gfxItems>
     <gfxBlacklistEntry  blockID="g35">      <os>WINNT 6.1</os>      <vendor>0x10de</vendor>              <devices>
                       <device>0x0a6c</device>
                   </devices>
             <feature>DIRECT2D</feature>      <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>      <driverVersion>8.17.12.5896</driverVersion>      <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator>    </gfxBlacklistEntry>
     <gfxBlacklistEntry  blockID="g36">      <os>WINNT 6.1</os>      <vendor>0x10de</vendor>              <devices>
@@ -3143,12 +3185,15 @@
       <serialNumber>ATE0vw==</serialNumber>
     </certItem>
         <certItem issuerName="MFkxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKjAoBgNVBAMTIVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPdmVyaGVpZCBDQQ">
       <serialNumber>ATFpsA==</serialNumber>
     </certItem>
         <certItem issuerName="MFkxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKjAoBgNVBAMTIVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPdmVyaGVpZCBDQQ">
       <serialNumber>ATFEdg==</serialNumber>
     </certItem>
+        <certItem issuerName="MDcxJDAiBgNVBAMTG1JDUyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEPMA0GA1UEChMGSFQgc3Js">
+      <serialNumber>AN9bfYOvlR1t</serialNumber>
+    </certItem>
       </certItems>
 
 
 </blocklist>
\ No newline at end of file
--- a/browser/app/permissions
+++ b/browser/app/permissions
@@ -1,21 +1,21 @@
 # This file has default permissions for the permission manager.
 # The file-format is strict:
 # * matchtype \t type \t permission \t host
-# * Only "host" is supported for matchtype
+# * "origin" should be used for matchtype, "host" is supported for legacy reasons
 # * type is a string that identifies the type of permission (e.g. "cookie")
 # * permission is an integer between 1 and 15
 # See nsPermissionManager.cpp for more...
 
 # UITour
-host	uitour	1	www.mozilla.org
-host	uitour	1	self-repair.mozilla.org
-host	uitour	1	support.mozilla.org
-host	uitour	1	about:home
+origin	uitour	1	https://www.mozilla.org
+origin	uitour	1	https://self-repair.mozilla.org
+origin	uitour	1	https://support.mozilla.org
+origin	uitour	1	about:home
 
 # XPInstall
-host	install	1	addons.mozilla.org
-host	install	1	marketplace.firefox.com
+origin	install	1	https://addons.mozilla.org
+origin	install	1	https://marketplace.firefox.com
 
 # Remote troubleshooting
-host	remote-troubleshooting	1	input.mozilla.org
-host	remote-troubleshooting	1	support.mozilla.org
+origin	remote-troubleshooting	1	https://input.mozilla.org
+origin	remote-troubleshooting	1	https://support.mozilla.org
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -402,17 +402,17 @@ pref("browser.search.defaultenginename",
 pref("browser.search.order.1",                "chrome://browser-region/locale/region.properties");
 pref("browser.search.order.2",                "chrome://browser-region/locale/region.properties");
 pref("browser.search.order.3",                "chrome://browser-region/locale/region.properties");
 
 // Market-specific search defaults
 // This is disabled globally, and then enabled for individual locales
 // in firefox-l10n.js (eg. it's enabled for en-US).
 pref("browser.search.geoSpecificDefaults", false);
-pref("browser.search.geoSpecificDefaults.url", "");
+pref("browser.search.geoSpecificDefaults.url", "https://search.services.mozilla.com/1/%APP%/%VERSION%/%CHANNEL%/%LOCALE%/%REGION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%");
 
 // US specific default (used as a fallback if the geoSpecificDefaults request fails).
 pref("browser.search.defaultenginename.US",      "data:text/plain,browser.search.defaultenginename.US=Yahoo");
 pref("browser.search.order.US.1",                "data:text/plain,browser.search.order.US.1=Yahoo");
 pref("browser.search.order.US.2",                "data:text/plain,browser.search.order.US.2=Google");
 pref("browser.search.order.US.3",                "data:text/plain,browser.search.order.US.3=Bing");
 
 // search bar results always open in a new tab
@@ -1858,16 +1858,19 @@ pref("identity.fxaccounts.settings.uri",
 pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox.com/v1");
 
 // The remote URL of the FxA OAuth Server
 pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
 
 // Whether we display profile images in the UI or not.
 pref("identity.fxaccounts.profile_image.enabled", true);
 
+// Token server used by the FxA Sync identity.
+pref("identity.sync.tokenserver.uri", "https://token.services.mozilla.com/1.0/sync/1.5");
+
 // Migrate any existing Firefox Account data from the default profile to the
 // Developer Edition profile.
 #ifdef MOZ_DEV_EDITION
 pref("identity.fxaccounts.migrateToDevEdition", true);
 #else
 pref("identity.fxaccounts.migrateToDevEdition", false);
 #endif
 
--- a/browser/base/content/aboutaccounts/aboutaccounts.js
+++ b/browser/base/content/aboutaccounts/aboutaccounts.js
@@ -90,16 +90,23 @@ function promptForRelink(acctName) {
 // a modal dialog checking they really really want to do this...
 // (This is sync-specific, so ideally would be in sync's identity module,
 // but it's a little more seamless to do here, and sync is currently the
 // only fxa consumer, so...
 function shouldAllowRelink(acctName) {
   return !needRelinkWarning(acctName) || promptForRelink(acctName);
 }
 
+function updateDisplayedEmail(user) {
+  let emailDiv = document.getElementById("email");
+  if (emailDiv && user) {
+    emailDiv.textContent = user.email;
+  }
+}
+
 let wrapper = {
   iframe: null,
 
   init: function (url, urlParams) {
     // If a master-password is enabled, we want to encourage the user to
     // unlock it.  Things still work if not, but the user will probably need
     // to re-auth next startup (in which case we will get here again and
     // re-prompt)
@@ -163,16 +170,17 @@ let wrapper = {
     setPreviousAccountNameHash(newAccountEmail);
 
     // A sync-specific hack - we want to ensure sync has been initialized
     // before we set the signed-in user.
     let xps = Cc["@mozilla.org/weave/service;1"]
               .getService(Ci.nsISupports)
               .wrappedJSObject;
     xps.whenLoaded().then(() => {
+      updateDisplayedEmail(accountData);
       return fxAccounts.setSignedInUser(accountData);
     }).then(() => {
       // If the user data is verified, we want it to immediately look like
       // they are signed in without waiting for messages to bounce around.
       if (accountData.verified) {
         show("stage", "manage");
       }
       this.injectData("message", { status: "login" });
@@ -196,17 +204,20 @@ let wrapper = {
   /**
    * onSessionStatus sends the currently signed in user's credentials
    * to the jelly.
    */
   onSessionStatus: function () {
     log("Received: 'session_status'.");
 
     fxAccounts.getSignedInUser().then(
-      (accountData) => this.injectData("message", { status: "session_status", data: accountData }),
+      (accountData) => {
+        updateDisplayedEmail(accountData);
+        this.injectData("message", { status: "session_status", data: accountData });
+      },
       (err) => this.injectData("message", { status: "error", error: err })
     );
   },
 
   /**
    * onSignOut handler erases the current user's session from the fxaccounts service
    */
   onSignOut: function () {
@@ -283,16 +294,18 @@ function openPrefs() {
 function init() {
   fxAccounts.getSignedInUser().then(user => {
     // tests in particular might cause the window to start closing before
     // getSignedInUser has returned.
     if (window.closed) {
       return;
     }
 
+    updateDisplayedEmail(user);
+
     // Ideally we'd use new URL(document.URL).searchParams, but for about: URIs,
     // searchParams is empty.
     let urlParams = new URLSearchParams(document.URL.split("?")[1] || "");
     let action = urlParams.get(ACTION_URL_PARAM);
     urlParams.delete(ACTION_URL_PARAM);
 
     switch (action) {
     case "signin":
@@ -322,18 +335,16 @@ function init() {
         show("remote");
         wrapper.init(url, urlParams);
       });
       break;
     default:
       // No action specified.
       if (user) {
         show("stage", "manage");
-        let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
-        document.title = sb.GetStringFromName("manage.pageTitle");
       } else {
         // Attempt a migration if enabled or show the introductory page
         // otherwise.
         migrateToDevEdition(urlParams).then(migrated => {
           if (!migrated) {
             show("stage", "intro");
             // load the remote frame in the background
             wrapper.init(fxAccounts.getAccountsSignUpURI(), urlParams);
@@ -389,16 +400,17 @@ function migrateToDevEdition(urlParams) 
   if (!migrateSyncCreds) {
     return Promise.resolve(false);
   }
 
   Cu.import("resource://gre/modules/osfile.jsm");
   let fxAccountsStorage = OS.Path.join(defaultProfilePath, fxAccountsCommon.DEFAULT_STORAGE_FILENAME);
   return OS.File.read(fxAccountsStorage, { encoding: "utf-8" }).then(text => {
     let accountData = JSON.parse(text).accountData;
+    updateDisplayedEmail(accountData);
     return fxAccounts.setSignedInUser(accountData);
   }).then(() => {
     return fxAccounts.promiseAccountsForceSigninURI().then(url => {
       show("remote");
       wrapper.init(url, urlParams);
     });
   }).then(null, error => {
     log("Failed to migrate FX Account: " + error);
--- a/browser/base/content/aboutaccounts/aboutaccounts.xhtml
+++ b/browser/base/content/aboutaccounts/aboutaccounts.xhtml
@@ -31,17 +31,18 @@
      href="chrome://browser/content/aboutaccounts/aboutaccounts.css"
      type="text/css" />
   </head>
   <body>
     <div id="stage">
 
       <div id="manage">
         <header>
-          <h1>&aboutAccounts.welcome;</h1>
+          <h1>&aboutAccounts.connected;</h1>
+          <div id="email"></div>
         </header>
 
         <section>
             <div class="graphic graphic-sync-intro"> </div>
 
             <div class="button-row">
               <button id="buttonOpenPrefs" class="button" href="#" tabindex="0">&aboutAccountsConfig.syncPreferences.label;</button>
             </div>
--- a/browser/base/content/aboutaccounts/main.css
+++ b/browser/base/content/aboutaccounts/main.css
@@ -47,19 +47,32 @@ a.no-underline {
   width: 420px;
 }
 
 header h1
 {
   font-size: 24px;
   font-weight: 200;
   line-height: 1em;
+}
+
+#intro header h1 {
   margin: 0 0 32px 0;
 }
 
+#manage header h1 {
+  margin: 0 0 12px 0;
+}
+
+#manage header #email {
+  margin-bottom: 23px;
+  color: rgb(138, 155, 168);
+  font-size: 19px;
+}
+
 .description {
   font-size: 18px;
 }
 
 .button-row {
   margin-top: 45px;
   margin-bottom:20px;
 }
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -42,25 +42,20 @@ const gXPInstallObserver = {
       }
       return;
     }
 
     const anchorID = "addons-notification-icon";
 
     // Make notifications persist a minimum of 30 seconds
     var options = {
-      timeout: Date.now() + 30000
+      displayURI: installInfo.originatingURI,
+      timeout: Date.now() + 30000,
     };
 
-    try {
-      options.displayOrigin = installInfo.originatingURI.host;
-    } catch (e) {
-      // originatingURI might be missing or 'host' might throw for non-nsStandardURL nsIURIs.
-    }
-
     let cancelInstallation = () => {
       if (installInfo) {
         for (let install of installInfo.installs)
           install.cancel();
       }
 
       this.acceptInstallation = null;
 
@@ -195,25 +190,20 @@ const gXPInstallObserver = {
 
     const anchorID = "addons-notification-icon";
     var messageString, action;
     var brandShortName = brandBundle.getString("brandShortName");
 
     var notificationID = aTopic;
     // Make notifications persist a minimum of 30 seconds
     var options = {
-      timeout: Date.now() + 30000
+      displayURI: installInfo.originatingURI,
+      timeout: Date.now() + 30000,
     };
 
-    try {
-      options.displayOrigin = installInfo.originatingURI.host;
-    } catch (e) {
-      // originatingURI might be missing or 'host' might throw for non-nsStandardURL nsIURIs.
-    }
-
     switch (aTopic) {
     case "addon-install-disabled": {
       notificationID = "xpinstall-disabled";
 
       if (gPrefService.prefIsLocked("xpinstall.enabled")) {
         messageString = gNavigatorBundle.getString("xpinstallDisabledMessageLocked");
         buttons = [];
       }
@@ -287,17 +277,23 @@ const gXPInstallObserver = {
         acceptButton.accessKey = gNavigatorBundle.getString("addonInstall.acceptButton.accesskey");
       } else {
         acceptButton.hidden = true;
       }
       break; }
     case "addon-install-failed": {
       // TODO This isn't terribly ideal for the multiple failure case
       for (let install of installInfo.installs) {
-        let host = options.displayOrigin;
+        let host;
+        try {
+          host  = options.displayURI.host;
+        } catch (e) {
+          // displayURI might be missing or 'host' might throw for non-nsStandardURL nsIURIs.
+        }
+
         if (!host)
           host = (install.sourceURI instanceof Ci.nsIStandardURL) &&
                  install.sourceURI.host;
 
         let error = (host || install.error == 0) ? "addonInstallError" : "addonLocalInstallError";
         let args;
         if (install.error < 0) {
           error += install.error;
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -39,24 +39,24 @@ var gPluginHandler = {
       this.uninit();
     }
   },
 
   receiveMessage: function (msg) {
     switch (msg.name) {
       case "PluginContent:ShowClickToPlayNotification":
         this.showClickToPlayNotification(msg.target, msg.data.plugins, msg.data.showNow,
-                                         msg.principal, msg.data.host, msg.data.location);
+                                         msg.principal, msg.data.location);
         break;
       case "PluginContent:RemoveNotification":
         this.removeNotification(msg.target, msg.data.name);
         break;
       case "PluginContent:UpdateHiddenPluginUI":
         this.updateHiddenPluginUI(msg.target, msg.data.haveInsecure, msg.data.actions,
-                                  msg.principal, msg.data.host, msg.data.location);
+                                  msg.principal, msg.data.location);
         break;
       case "PluginContent:HideNotificationBar":
         this.hideNotificationBar(msg.target, msg.data.name);
         break;
       case "PluginContent:ShowInstallNotification":
         return this.showInstallNotification(msg.target, msg.data.pluginInfo);
       case "PluginContent:InstallSinglePlugin":
         this.installSinglePlugin(msg.data.pluginInfo);
@@ -211,18 +211,18 @@ var gPluginHandler = {
     }
 
     browser.messageManager.sendAsyncMessage("BrowserPlugins:ActivatePlugins", {
       pluginInfo: aPluginInfo,
       newState: aNewState,
     });
   },
 
-  showClickToPlayNotification: function (browser, plugins, showNow, principal,
-                                         host, location) {
+  showClickToPlayNotification: function (browser, plugins, showNow,
+                                         principal, location) {
     // It is possible that we've received a message from the frame script to show
     // a click to play notification for a principal that no longer matches the one
     // that the browser's content now has assigned (ie, the browser has browsed away
     // after the message was sent, but before the message was received). In that case,
     // we should just ignore the message.
     if (!principal.equals(browser.contentPrincipal)) {
       return;
     }
@@ -290,17 +290,16 @@ var gPluginHandler = {
     }
 
     let options = {
       dismissed: !showNow,
       eventCallback: this._clickToPlayNotificationEventCallback,
       primaryPlugin: primaryPluginPermission,
       pluginData: pluginData,
       principal: principal,
-      host: host,
     };
     PopupNotifications.show(browser, "click-to-play-plugins",
                             "", "plugins-notification-icon",
                             null, null, options);
     browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationShown");
   },
 
   removeNotification: function (browser, name) {
@@ -311,18 +310,20 @@ var gPluginHandler = {
 
   hideNotificationBar: function (browser, name) {
     let notificationBox = gBrowser.getNotificationBox(browser);
     let notification = notificationBox.getNotificationWithValue(name);
     if (notification)
       notificationBox.removeNotification(notification, true);
   },
 
-  updateHiddenPluginUI: function (browser, haveInsecure, actions, principal,
-                                  host, location) {
+  updateHiddenPluginUI: function (browser, haveInsecure, actions,
+                                  principal, location) {
+    let origin = principal.originNoSuffix;
+
     // It is possible that we've received a message from the frame script to show
     // the hidden plugin notification for a principal that no longer matches the one
     // that the browser's content now has assigned (ie, the browser has browsed away
     // after the message was sent, but before the message was received). In that case,
     // we should just ignore the message.
     if (!principal.equals(browser.contentPrincipal)) {
       return;
     }
@@ -375,32 +376,32 @@ var gPluginHandler = {
       if (actions.length == 1) {
         let pluginInfo = actions[0];
         let pluginName = pluginInfo.pluginName;
 
         switch (pluginInfo.fallbackType) {
           case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
             message = gNavigatorBundle.getFormattedString(
               "pluginActivateNew.message",
-              [pluginName, host]);
+              [pluginName, origin]);
             break;
           case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
             message = gNavigatorBundle.getFormattedString(
               "pluginActivateOutdated.message",
-              [pluginName, host, brand]);
+              [pluginName, origin, brand]);
             break;
           case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
             message = gNavigatorBundle.getFormattedString(
               "pluginActivateVulnerable.message",
-              [pluginName, host, brand]);
+              [pluginName, origin, brand]);
         }
       } else {
         // Multi-plugin
         message = gNavigatorBundle.getFormattedString(
-          "pluginActivateMultiple.message", [host]);
+          "pluginActivateMultiple.message", [origin]);
       }
 
       let buttons = [
         {
           label: gNavigatorBundle.getString("pluginContinueBlocking.label"),
           accessKey: gNavigatorBundle.getString("pluginContinueBlocking.accesskey"),
           callback: function() {
             Services.telemetry.getHistogramById("PLUGINS_INFOBAR_BLOCK").
--- a/browser/base/content/browser-trackingprotection.js
+++ b/browser/base/content/browser-trackingprotection.js
@@ -94,17 +94,21 @@ let TrackingProtection = {
 
     BrowserReload();
   },
 
   enableForCurrentPage() {
     // Remove the current host from the 'trackingprotection' consumer
     // of the permission manager. This effectively removes this host
     // from the tracking protection allowlist.
-    Services.perms.remove(gBrowser.selectedBrowser.currentURI,
+    let normalizedUrl = Services.io.newURI(
+      "https://" + gBrowser.selectedBrowser.currentURI.hostPort,
+      null, null);
+
+    Services.perms.remove(normalizedUrl,
       "trackingprotection");
 
     // Telemetry for enable protection.
     this.eventsHistogram.add(2);
 
     BrowserReload();
   },
 };
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -1353,8 +1353,15 @@ toolbarpaletteitem[place="palette"][hidd
 
 #password-notification-password:hover::after {
   color: hsl(210,100%,50%);
 }
 
 #password-notification-password[focused]::after {
   content: none;
 }
+
+/* Bug 1175941: Disable the transition on 10.10 due to flickering, possibly due to an OS X bug. */
+@media (-moz-mac-yosemite-theme) {
+  #password-notification-password::after {
+    transition: none;
+  }
+}
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1417,16 +1417,30 @@ var gBrowserInit = {
     // Delay this a minute because there's no rush
     setTimeout(() => {
       this.gmpInstallManager = new GMPInstallManager();
       // We don't really care about the results, if someone is interested they
       // can check the log.
       this.gmpInstallManager.simpleCheckAndInstall().then(null, () => {});
     }, 1000 * 60);
 
+    // Report via telemetry whether we're able to play MP4/H.264/AAC video.
+    // We suspect that some Windows users have a broken or have not installed
+    // Windows Media Foundation, and we'd like to know how many. We'd also like
+    // to know how good our coverage is on other platforms.
+    // Note: we delay by 90 seconds reporting this, as calling canPlayType()
+    // on Windows will cause DLLs to load, i.e. cause disk I/O.
+    setTimeout(() => {
+      let v = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
+      let aacWorks = v.canPlayType("audio/mp4") != "";
+      Services.telemetry.getHistogramById("VIDEO_CAN_CREATE_AAC_DECODER").add(aacWorks);
+      let h264Works = v.canPlayType("video/mp4") != "";
+      Services.telemetry.getHistogramById("VIDEO_CAN_CREATE_H264_DECODER").add(h264Works);
+    }, 90 * 1000);
+
     SessionStore.promiseInitialized.then(() => {
       // Bail out if the window has been closed in the meantime.
       if (window.closed) {
         return;
       }
 
       // Enable the Restore Last Session command if needed
       RestoreLastSessionObserver.init();
@@ -5155,16 +5169,18 @@ var TabsInTitlebar = {
       if (tabAndMenuHeight > titlebarContentHeight) {
         // We need to increase the titlebar content's outer height (ie including margins)
         // to match the tab and menu height:
         let extraMargin = tabAndMenuHeight - titlebarContentHeight;
 #ifndef XP_MACOSX
         titlebarContent.style.marginBottom = extraMargin + "px";
 #endif
         titlebarContentHeight += extraMargin;
+      } else {
+        titlebarContent.style.removeProperty("margin-bottom");
       }
 
       // Then we bring up the titlebar by the same amount, but we add any negative margin:
       titlebar.style.marginBottom = "-" + titlebarContentHeight + "px";
 
 
       // Finally, size the placeholders:
 #ifdef XP_MACOSX
@@ -7085,35 +7101,31 @@ var gIdentityHandler = {
 
     // Update the popup strings
     this.setPopupMessages(this._identityBox.className);
 
     this.updateSitePermissions();
 
     // Add the "open" attribute to the identity box for styling
     this._identityBox.setAttribute("open", "true");
-    var self = this;
-    this._identityPopup.addEventListener("popuphidden", function onPopupHidden(e) {
-      e.currentTarget.removeEventListener("popuphidden", onPopupHidden, false);
-      self._identityBox.removeAttribute("open");
-    }, false);
 
     // Now open the popup, anchored off the primary chrome element
     this._identityPopup.openPopup(this._identityIcon, "bottomcenter topleft");
   },
 
   onPopupShown(event) {
     if (event.target == this._identityPopup) {
       window.addEventListener("focus", this, true);
     }
   },
 
   onPopupHidden(event) {
     if (event.target == this._identityPopup) {
       window.removeEventListener("focus", this, true);
+      this._identityBox.removeAttribute("open");
     }
   },
 
   handleEvent(event) {
     let elem = document.activeElement;
     let position = elem.compareDocumentPosition(this._identityPopup);
 
     if (!(position & (Node.DOCUMENT_POSITION_CONTAINS |
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -1112,18 +1112,19 @@ var imagePermissionObserver = {
 
     if (aTopic == "perm-changed") {
       var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission);
       if (permission.type == "image") {
         var imageTree = document.getElementById("imagetree");
         var row = getSelectedRow(imageTree);
         var item = gImageView.data[row][COL_IMAGE_NODE];
         var url = gImageView.data[row][COL_IMAGE_ADDRESS];
-        if (makeURI(url).host == permission.host)
+        if (permission.matchesURI(makeURI(url), true)) {
           makeBlockImage(url);
+        }
       }
     }
   }
 }
 
 function getContentTypeFromHeaders(cacheEntryDescriptor)
 {
   if (!cacheEntryDescriptor)
--- a/browser/base/content/pageinfo/permissions.js
+++ b/browser/base/content/pageinfo/permissions.js
@@ -13,34 +13,34 @@ var gUsageRequest;
 var gPermissions = SitePermissions.listPermissions();
 gPermissions.push("plugins");
 
 var permissionObserver = {
   observe: function (aSubject, aTopic, aData)
   {
     if (aTopic == "perm-changed") {
       var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission);
-      if (permission.host == gPermURI.host) {
+      if (permission.matchesURI(gPermURI, true)) {
         if (gPermissions.indexOf(permission.type) > -1)
           initRow(permission.type);
         else if (permission.type.startsWith("plugin"))
           setPluginsRadioState();
       }
     }
   }
 };
 
 function onLoadPermission()
 {
   var uri = BrowserUtils.makeURIFromCPOW(gDocument.documentURIObject);
   var permTab = document.getElementById("permTab");
   if (SitePermissions.isSupportedURI(uri)) {
     gPermURI = uri;
     var hostText = document.getElementById("hostText");
-    hostText.value = gPermURI.host;
+    hostText.value = gPermURI.prePath;
 
     for (var i of gPermissions)
       initRow(i);
     var os = Components.classes["@mozilla.org/observer-service;1"]
                        .getService(Components.interfaces.nsIObserverService);
     os.addObserver(permissionObserver, "perm-changed", false);
     onUnloadRegistry.push(onUnloadPermission);
     permTab.hidden = false;
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1921,17 +1921,17 @@
                 this._lastRelatedTab.owner = null;
               else
                 t.owner = this.selectedTab;
               this.moveTabTo(t, newTabPos);
               this._lastRelatedTab = t;
             }
 
             if (animate) {
-              mozRequestAnimationFrame(function () {
+              requestAnimationFrame(function () {
                 this.tabContainer._handleTabTelemetryStart(t, aURI);
 
                 // kick the animation off
                 t.setAttribute("fadein", "true");
               }.bind(this));
             }
 
             return t;
--- a/browser/base/content/test/general/browser_bug585785.js
+++ b/browser/base/content/test/general/browser_bug585785.js
@@ -2,17 +2,17 @@ var tab;
 
 function test() {
   waitForExplicitFinish();
 
   tab = gBrowser.addTab();
   isnot(tab.getAttribute("fadein"), "true", "newly opened tab is yet to fade in");
 
   // Try to remove the tab right before the opening animation's first frame
-  window.mozRequestAnimationFrame(checkAnimationState);
+  window.requestAnimationFrame(checkAnimationState);
 }
 
 function checkAnimationState() {
   is(tab.getAttribute("fadein"), "true", "tab opening animation initiated");
 
   info(window.getComputedStyle(tab).maxWidth);
   gBrowser.removeTab(tab, { animate: true });
   if (!tab.parentNode) {
--- a/browser/base/content/test/general/browser_bug592338.js
+++ b/browser/base/content/test/general/browser_bug592338.js
@@ -42,17 +42,17 @@ function test_install_http() {
     });
   }, false);
 },
 
 function test_install_lwtheme() {
   is(LightweightThemeManager.currentTheme, null, "Should be no lightweight theme selected");
 
   var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+  pm.add(makeURI("https://example.com/"), "install", pm.ALLOW_ACTION);
 
   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);
 
--- a/browser/base/content/test/general/browser_parsable_css.js
+++ b/browser/base/content/test/general/browser_parsable_css.js
@@ -16,16 +16,19 @@ const kWhitelist = [
   {sourceName: /codemirror\.css/i},
   // PDFjs is futureproofing its pseudoselectors, and those rules are dropped.
   {sourceName: /web\/viewer\.css/i,
    errorMessage: /Unknown pseudo-class.*(fullscreen|selection)/i},
   // Tracked in bug 1004428.
   {sourceName: /aboutaccounts\/(main|normalize)\.css/i},
   // TokBox SDK assets, see bug 1032469.
   {sourceName: /loop\/.*sdk-content\/.*\.css$/i},
+  // Loop standalone client CSS uses placeholder cross browser pseudo-element
+  {sourceName: /loop\/.*\.css/i,
+   errorMessage: /Unknown pseudo-class.*placeholder/i},
   // Highlighter CSS uses chrome-only pseudo-class, see bug 985597.
   {sourceName: /highlighter\.css/i,
    errorMessage: /Unknown pseudo-class.*moz-native-anonymous/i},
 ];
 
 let moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm");
 let {generateURIsFromDirTree} = Cu.import(moduleLocation, {});
 
--- a/browser/base/content/test/general/browser_trackingUI_1.js
+++ b/browser/base/content/test/general/browser_trackingUI_1.js
@@ -53,27 +53,35 @@ function testBenignPage() {
   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() {
+function testTrackingPage(window) {
   info("Tracking content must be blocked");
   ok (!TrackingProtection.container.hidden, "The container is visible");
   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");
+
+
+  if (PrivateBrowsingUtils.isWindowPrivate(window)) {
+    ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
+    ok(!hidden("#tracking-action-unblock-private"), "unblockButtonPrivate is visible");
+  } else {
+    ok(!hidden("#tracking-action-unblock"), "unblockButton is visible");
+    ok(hidden("#tracking-action-unblock-private"), "unblockButtonPrivate 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 hidden");
   ok (!hidden("#tracking-blocked"), "labelTrackingBlocked is visible");
 }
 
 function testTrackingPageUnblocked() {
@@ -96,29 +104,29 @@ function testTrackingPageUnblocked() {
 
 function* testTrackingProtectionForTab(tab) {
   info("Load a test page not containing tracking elements");
   yield promiseTabLoadEvent(tab, BENIGN_PAGE);
   testBenignPage();
 
   info("Load a test page containing tracking elements");
   yield promiseTabLoadEvent(tab, TRACKING_PAGE);
-  testTrackingPage();
+  testTrackingPage(tab.ownerDocument.defaultView);
 
   info("Disable TP for the page (which reloads the page)");
   let tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-unblock");
   yield tabReloadPromise;
   testTrackingPageUnblocked();
 
   info("Re-enable TP for the page (which reloads the page)");
   tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-block");
   yield tabReloadPromise;
-  testTrackingPage();
+  testTrackingPage(tab.ownerDocument.defaultView);
 }
 
 add_task(function* testNormalBrowsing() {
   yield UrlClassifierTestUtils.addTestTrackers();
 
   browser = gBrowser;
   let tab = browser.selectedTab = browser.addTab();
 
--- a/browser/base/content/test/plugins/browser_CTP_hide_overlay.js
+++ b/browser/base/content/test/plugins/browser_CTP_hide_overlay.js
@@ -24,37 +24,33 @@ add_task(function* () {
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
 
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gBrowser.selectedBrowser);
 
-  // Tests that the overlay can be hidded for disabled plugins using the close icon.
-  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+  // Tests that the overlay can be hidden for plugins using the close icon.
+  let overlayIsVisible = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
     let closeIcon = doc.getAnonymousElementByAttribute(plugin, "anonid", "closeIcon")
     let bounds = closeIcon.getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIDOMWindowUtils);
     utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
+
+    return overlay.classList.contains("visible");
   });
 
-  let overlayIsVisible = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
-    let doc = content.document;
-    let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return plugin && overlay.classList.contains("visible");
-  });
   ok(!overlayIsVisible, "overlay should be hidden.");
 });
 
 // Test that the overlay cannot be interacted with after the user closes the overlay
 add_task(function* () {
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
 
@@ -79,16 +75,16 @@ add_task(function* () {
     // Simulate clicking on the close icon.
     utils.sendMouseEvent("mousedown", closeIconLeft, closeIconTop, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", closeIconLeft, closeIconTop, 0, 1, 0, false, 0, 0);
 
     // Simulate clicking on the overlay.
     utils.sendMouseEvent("mousedown", overlayLeft, overlayTop, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", overlayLeft, overlayTop, 0, 1, 0, false, 0, 0);
 
-    return plugin && !overlay.classList.contains("visible");
+    return overlay.hasAttribute("dismissed") && !overlay.classList.contains("visible");
   });
 
   let notification = PopupNotifications.getNotification("click-to-play-plugins");
 
   ok(notification.dismissed, "No notification should be shown");
   ok(overlayHidden, "Overlay should be hidden");
 });
--- a/browser/base/content/test/plugins/head.js
+++ b/browser/base/content/test/plugins/head.js
@@ -233,17 +233,17 @@ function promiseReloadPlugin(aId, aBrows
 
 // after a test is done using the plugin doorhanger, we should just clear
 // any permissions that may have crept in
 function clearAllPluginPermissions() {
   let perms = Services.perms.enumerator;
   while (perms.hasMoreElements()) {
     let perm = perms.getNext();
     if (perm.type.startsWith('plugin')) {
-      info("removing permission:" + perm.host + " " + perm.type + "\n");
+      info("removing permission:" + perm.principal.origin + " " + perm.type + "\n");
       Services.perms.removePermission(perm);
     }
   }
 }
 
 function updateBlocklist(aCallback) {
   let blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"]
                           .getService(Ci.nsITimerCallback);
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -2427,30 +2427,33 @@ file, You can obtain one at http://mozil
       <method name="disableTrackingContentProtection">
         <body><![CDATA[
           // convert document URI into the format used by
           // nsChannelClassifier::ShouldEnableTrackingProtection
           // (any scheme turned into https is correct)
           let normalizedUrl = Services.io.newURI(
             "https://" + gBrowser.selectedBrowser.currentURI.hostPort,
             null, null);
-          // Add the current host in the 'trackingprotection' consumer of
+          // Add the current host/port combination in the 'trackingprotection' consumer of
           // the permission manager using a normalized URI. This effectively
-          // places this host on the tracking protection allowlist.
+          // places this host/port combination on the tracking protection allowlist.
           Services.perms.add(normalizedUrl,
             "trackingprotection", Services.perms.ALLOW_ACTION);
           BrowserReload();
         ]]></body>
       </method>
       <method name="enableTrackingContentProtection">
         <body><![CDATA[
-          // Remove the current host from the 'trackingprotection' consumer
-          // of the permission manager. This effectively removes this host
+          // Remove the current host/port combination from the 'trackingprotection' consumer
+          // of the permission manager. This effectively removes this host/port combination
           // from the tracking protection allowlist.
-          Services.perms.remove(gBrowser.selectedBrowser.currentURI,
+          let normalizedUrl = Services.io.newURI(
+            "https://" + gBrowser.selectedBrowser.currentURI.hostPort,
+            null, null);
+          Services.perms.remove(normalizedUrl,
             "trackingprotection");
           BrowserReload();
         ]]></body>
       </method>
     </implementation>
   </binding>
 
   <binding id="click-to-play-plugins-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
@@ -2556,18 +2559,18 @@ file, You can obtain one at http://mozil
           var grid = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-center-box");
 
           if (this._states.SINGLE == state) {
             grid.hidden = true;
             this._setupSingleState();
             return;
           }
 
-          let host = this.notification.options.host;
-          this._setupDescription("pluginActivateMultiple.message", null, host);
+          let prePath = this.notification.options.principal.URI.prePath;
+          this._setupDescription("pluginActivateMultiple.message", null, prePath);
 
           var showBox = document.getAnonymousElementByAttribute(this, "anonid", "plugin-notification-showbox");
 
           var dialogStrings = Services.strings.createBundle("chrome://global/locale/dialog.properties");
           this._primaryButton.label = dialogStrings.GetStringFromName("button-accept");
           this._primaryButton.setAttribute("default", "true");
 
           this._secondaryButton.label = dialogStrings.GetStringFromName("button-cancel");
@@ -2596,17 +2599,17 @@ file, You can obtain one at http://mozil
             showBox.hidden = true;
           }
           this._setupLink(null);
         ]]></body>
       </method>
       <method name="_setupSingleState">
         <body><![CDATA[
           var action = this._items[0].action;
-          var host = action.pluginPermissionHost;
+          var prePath = action.pluginPermissionPrePath;
 
           let label, linkLabel, linkUrl, button1, button2;
 
           if (action.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
             button1 = {
               label: "pluginBlockNow.label",
               accesskey: "pluginBlockNow.accesskey",
               action: "_singleBlock"
@@ -2691,17 +2694,17 @@ file, You can obtain one at http://mozil
               linkLabel = "pluginActivate.riskLabel"
               button1.default = true;
               break;
 
             default:
               Cu.reportError(Error("Unexpected blocklist state"));
             }
           }
-          this._setupDescription(label, action.pluginName, host);
+          this._setupDescription(label, action.pluginName, prePath);
           this._setupLink(linkLabel, action.detailsLink);
 
           this._primaryButton.label = gNavigatorBundle.getString(button1.label);
           this._primaryButton.accessKey = gNavigatorBundle.getString(button1.accesskey);
           this._primaryButton.setAttribute("action", button1.action);
 
           this._secondaryButton.label = gNavigatorBundle.getString(button2.label);
           this._secondaryButton.accessKey = gNavigatorBundle.getString(button2.accesskey);
@@ -2712,35 +2715,35 @@ file, You can obtain one at http://mozil
           else if (button2.default) {
             this._secondaryButton.setAttribute("default", "true");
           }
         ]]></body>
       </method>
       <method name="_setupDescription">
         <parameter name="baseString" />
         <parameter name="pluginName" /> <!-- null for the multiple-plugin case -->
-        <parameter name="host" />
+        <parameter name="prePath" />
         <body><![CDATA[
           var bsn = this._brandShortName;
           var span = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-description");
           while (span.lastChild) {
             span.removeChild(span.lastChild);
           }
 
-          var args = ["__host__", this._brandShortName];
+          var args = ["__prepath__", this._brandShortName];
           if (pluginName) {
             args.unshift(pluginName);
           }
           var bases = gNavigatorBundle.getFormattedString(baseString, args).
-            split("__host__", 2);
+            split("__prepath__", 2);
 
           span.appendChild(document.createTextNode(bases[0]));
-          var hostSpan = document.createElementNS("http://www.w3.org/1999/xhtml", "em");
-          hostSpan.appendChild(document.createTextNode(host));
-          span.appendChild(hostSpan);
+          var prePathSpan = document.createElementNS("http://www.w3.org/1999/xhtml", "em");
+          prePathSpan.appendChild(document.createTextNode(prePath));
+          span.appendChild(prePathSpan);
           span.appendChild(document.createTextNode(bases[1] + " "));
         ]]></body>
       </method>
       <method name="_setupLink">
         <parameter name="linkString"/>
         <parameter name="linkUrl" />
         <body><![CDATA[
           var link = document.getAnonymousElementByAttribute(this, "anonid", "click-to-play-plugins-notification-link");
@@ -3108,19 +3111,19 @@ file, You can obtain one at http://mozil
           if (!this._notificationType) {
             return;
           }
 
           let viewsLeft = this._viewsLeft;
           if (viewsLeft) {
             let notification = this._panel.firstElementChild.notification;
             if (this._notificationType == "passwords" && notification && notification.options &&
-                notification.options.origin) {
+                notification.options.displayURI instanceof Ci.nsIStandardURL) {
               let fxAOrigin = new URL(Services.prefs.getCharPref("identity.fxaccounts.remote.signup.uri")).origin
-              if (notification.options.origin == fxAOrigin) {
+              if (notification.options.displayURI.prePath == fxAOrigin) {
                 // Somewhat gross hack - we don't want to show the sync promo while
                 // the user may be logging into Sync.
                 return;
               }
             }
 
             if (Services.prefs.prefHasUserValue("services.sync.username") &&
                this._notificationType != "addons-sync-disabled") {
--- a/browser/components/controlcenter/content/panel.inc.xul
+++ b/browser/components/controlcenter/content/panel.inc.xul
@@ -39,33 +39,38 @@
       <hbox id="tracking-protection-container" class="identity-popup-section">
         <vbox id="tracking-protection-content" flex="1">
           <description class="identity-popup-text identity-popup-headline"
                        crop="end"
                        value="&trackingProtection.title;" />
 
           <label id="tracking-blocked"
                  class="identity-popup-text"
-                 crop="end">&trackingProtection.detectedBlocked;</label>
+                 crop="end">&trackingProtection.detectedBlocked2;</label>
           <label id="tracking-loaded"
                  class="identity-popup-text"
-                 crop="end">&trackingProtection.detectedNotBlocked;</label>
+                 crop="end">&trackingProtection.detectedNotBlocked2;</label>
           <label id="tracking-not-detected"
                  class="identity-popup-text"
-                 crop="end">&trackingProtection.notDetected;</label>
+                 crop="end">&trackingProtection.notDetected2;</label>
 
           <button id="tracking-action-unblock"
                   label="&trackingProtection.unblock.label;"
                   class="identity-popup-button"
                   accesskey="&trackingProtection.unblock.accesskey;"
                   oncommand="TrackingProtection.disableForCurrentPage();" />
+          <button id="tracking-action-unblock-private"
+                  label="&trackingProtection.unblockPrivate.label;"
+                  class="identity-popup-button"
+                  accesskey="&trackingProtection.unblock.accesskey;"
+                  oncommand="TrackingProtection.disableForCurrentPage();" />
           <button id="tracking-action-block"
-                  label="&trackingProtection.block.label;"
+                  label="&trackingProtection.block2.label;"
                   class="identity-popup-button"
-                  accesskey="&trackingProtection.block.accesskey;"
+                  accesskey="&trackingProtection.block2.accesskey;"
                   oncommand="TrackingProtection.enableForCurrentPage();" />
         </vbox>
       </hbox>
 
       <!-- Permissions Section -->
       <hbox id="identity-popup-permissions" class="identity-popup-section">
         <vbox id="identity-popup-permissions-content" flex="1">
           <label class="identity-popup-text identity-popup-headline"
--- a/browser/components/customizableui/test/browser_946320_tabs_from_other_computers.js
+++ b/browser/components/customizableui/test/browser_946320_tabs_from_other_computers.js
@@ -82,17 +82,17 @@ function configureFxAccountIdentity() {
     kA: "kA",
     kB: "kB",
     sessionToken: "sessionToken",
     uid: "user_uid",
     verified: true,
   };
 
   let token = {
-    endpoint: Weave.Svc.Prefs.get("tokenServerURI"),
+    endpoint: null,
     duration: 300,
     id: "id",
     key: "key",
     // uid will be set to the username.
   };
 
   let MockInternal = {};
   let mockTSC = { // TokenServerClient
--- a/browser/components/loop/content/css/panel.css
+++ b/browser/components/loop/content/css/panel.css
@@ -210,17 +210,17 @@ body {
 .rooms > h1 {
   font-weight: bold;
   color: #999;
   padding: .5rem 0;
 }
 
 .new-room-view > .context {
   margin: .5rem 0 0;
-  background-color: #DEEFF7;
+  background-color: #dbf7ff;
   border-radius: 3px 3px 0 0;
   padding: .5rem;
 }
 
 .new-room-view > .context > .context-enabled {
   margin-bottom: .5rem;
   display: block;
 }
@@ -238,17 +238,17 @@ body {
   background-color: #fff;
 }
 
 .new-room-view > .context > .checkbox-wrapper > .checkbox.checked {
   background-image: url("../shared/img/check.svg#check-blue");
 }
 
 .new-room-view > .context > .checkbox-wrapper > label {
-  color: #3c3c3c;
+  color: #333;
   font-weight: 700;
 }
 
 .new-room-view > .btn {
   display: block;
   font-size: 1rem;
   margin: 0 auto .5rem;
   width: 100%;
--- a/browser/components/loop/content/shared/css/common.css
+++ b/browser/components/loop/content/shared/css/common.css
@@ -515,17 +515,17 @@ html[dir="rtl"] .context-content {
 
 .context-content > p {
   font-weight: bold;
   margin-bottom: .8em;
   margin-top: 0;
 }
 
 .context-wrapper {
-  border: 1px solid #0096dd;
+  border: 1px solid #5cccee;
   border-radius: 3px;
   background: #fff;
   padding: .8em;
   /* Use the flex row mode to position the elements next to each other. */
   display: flex;
   flex-flow: row nowrap;
   line-height: 1.1em;
 }
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -1460,38 +1460,43 @@ html[dir="rtl"] .standalone .room-conver
 
 .text-chat-entries {
   overflow: auto;
 }
 
 .text-chat-entry {
   display: flex;
   flex-direction: row;
+  margin-right: .2em;
   margin-bottom: .5em;
-  text-align: start;
+  text-align: end;
   flex-wrap: nowrap;
   justify-content: flex-start;
   align-content: stretch;
   align-items: flex-start;
 }
 
+html[dir="rtl"] .text-chat-entry {
+  margin-right: auto;
+  margin-left: .2em;
+}
+
 .text-chat-entry > p {
   position: relative;
   z-index: 10;
   /* Drop the default margins from the 'p' element. */
   margin: 0;
   padding: .7em;
   /* leave some room for the chat bubble arrow */
   max-width: 80%;
   border-width: 1px;
   border-style: solid;
-  border-color: #2ea4ff;
+  border-color: #5cccee;
   background: #fff;
   word-wrap: break-word;
-  word-wrap: break-word;
   flex: 0 1 auto;
   align-self: auto;
 }
 
 .text-chat-entry.sent > p,
 .text-chat-entry.received > p {
   background: #fff;
 }
@@ -1529,17 +1534,17 @@ html[dir="rtl"] .text-chat-entry.receive
 }
 
 .text-chat-entry.received > p {
   border-color: #d8d8d8;
 }
 
 /* Text chat entry timestamp */
 .text-chat-entry-timestamp {
-  margin: 0 .2em;
+  margin: 0 .5em;
   color: #aaa;
   font-style: italic;
   font-size: .8em;
   order: 0;
   flex: 0 1 auto;
   align-self: center;
 }
 
@@ -1664,17 +1669,42 @@ html[dir="rtl"] .text-chat-entry.receive
 }
 
 .text-chat-box > form > input {
   width: 100%;
   height: 40px;
   padding: 0 .5em .5em;
   font-size: 1.1em;
   border: 0;
-  border-top: 1px solid #999;
+  border-top: 1px solid #d8d8d8;
+}
+
+.text-chat-box > form > input::-webkit-input-placeholder {
+  font-size: 1.1em;
+  color: #999;
+}
+
+.text-chat-box > form > input::-moz-placeholder {
+  font-size: 1.1em;
+  color: #999;
+}
+
+.text-chat-box > form > input:-moz-placeholder {
+  font-size: 1.1em;
+  color: #999;
+}
+
+.text-chat-box > form > input:-ms-input-placeholder {
+  font-size: 1.1em;
+  color: #999;
+}
+
+.text-chat-box > form > input:input-placeholder {
+  font-size: 1.1em;
+  color: #999;
 }
 
 /* turn the visible border blue as a visual indicator of focus */
 .text-chat-box > form > input:focus {
   border-top: 1px solid #66c9f2;
 }
 
 @media screen and (max-width:640px) {
--- a/browser/components/loop/content/shared/img/chatbubble-arrow-right.svg
+++ b/browser/components/loop/content/shared/img/chatbubble-arrow-right.svg
@@ -1,14 +1,14 @@
 <?xml version="1.0"?>
 <svg width="20" height="9" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
  <title>chatbubble-arrow</title>
  <desc>Created with Sketch.</desc>
  <g>
   <title>Layer 1</title>
   <g id="svg_1" fill="none">
-   <path id="svg_2" fill="#2EA4FF" d="m19.505243,8.972466l-9.299002,0l0,-1l6.088001,0c-2.110002,-0.967 -4.742001,-2.818 -6.088001,-6.278l0.932,-0.363c2.202,5.664 8.377999,6.637 8.44,6.646c0.259001,0.039 0.444,0.27 0.426001,0.531c-0.019001,0.262 -0.237,0.464 -0.498999,0.464l-12.072001,-0.352"/>
+   <path id="svg_2" fill="#5cccee" d="m19.505243,8.972466l-9.299002,0l0,-1l6.088001,0c-2.110002,-0.967 -4.742001,-2.818 -6.088001,-6.278l0.932,-0.363c2.202,5.664 8.377999,6.637 8.44,6.646c0.259001,0.039 0.444,0.27 0.426001,0.531c-0.019001,0.262 -0.237,0.464 -0.498999,0.464l-12.072001,-0.352"/>
   </g>
   <line id="svg_13" y2="8.474788" x2="6.200791" y1="8.474788" x1="10.265923" stroke="#22a4ff" fill="none"/>
   <line id="svg_26" y2="8.474788" x2="2.14584" y1="8.474788" x1="6.210972" stroke="#22a4ff" fill="none"/>
   <line id="svg_27" y2="8.474788" x2="0.000501" y1="8.474788" x1="4.065633" stroke="#22a4ff" fill="none"/>
  </g>
 </svg>
\ No newline at end of file
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -737,17 +737,30 @@ nsDefaultCommandLineHandler.prototype = 
     try {
       var ar;
       while ((ar = cmdLine.handleFlagWithParam("url", false))) {
         var uri = resolveURIInternal(cmdLine, ar);
 
         // Searches in the Windows 10 task bar searchbox simply open the default browser
         // with a URL for a search on Bing. Here we extract the search term and use the
         // user's default search engine instead.
-        if (redirectWinSearch && uri.spec.startsWith("https://www.bing.com/search")) {
+        var uriScheme = "", uriHost = "", uriPath = "";
+        try {
+          uriScheme = uri.scheme;
+          uriHost = uri.host;
+          uriPath = uri.path;
+        } catch(e) {
+        }
+
+        // Most Windows searches are "https://www.bing.com/search...", but bug
+        // 1182308 reports a Chinese edition of Windows 10 using
+        // "http://cn.bing.com/search...", so be a bit flexible in what we match.
+        if (redirectWinSearch &&
+            (uriScheme == "http" || uriScheme == "https") &&
+            uriHost.endsWith(".bing.com") && uriPath.startsWith("/search")) {
           try {
             var url = uri.QueryInterface(Components.interfaces.nsIURL);
             var params = new URLSearchParams(url.query);
             // We don't want to rewrite all Bing URLs coming from external apps. Look
             // for the magic URL parm that's present in searches from the task bar.
             // (Typed searches use "form=WNSGPH", Cortana voice searches use "FORM=WNSBOX")
             var formParam = params.get("form");
             if (!formParam) {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2435,19 +2435,17 @@ ContentPermissionPrompt.prototype = {
     let types = aRequest.types.QueryInterface(Ci.nsIArray);
     if (types.length != 1) {
       aRequest.cancel();
       return;
     }
 
     if (!aOptions)
       aOptions = {};
-    aOptions.displayOrigin = (requestPrincipal.URI instanceof Ci.nsIFileURL) ?
-                             requestPrincipal.URI.file.path :
-                             requestPrincipal.URI.host;
+    aOptions.displayOrigin = requestPrincipal.URI;
 
     return chromeWin.PopupNotifications.show(browser, aNotificationId, aMessage, aAnchorId,
                                              mainAction, secondaryActions, aOptions);
   },
 
   _promptPush : function(aRequest) {
     var message = gBrowserBundle.GetStringFromName("push.enablePush2");
 
--- a/browser/components/preferences/aboutPermissions.js
+++ b/browser/components/preferences/aboutPermissions.js
@@ -12,26 +12,29 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/DownloadUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/ForgetAboutSite.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
 
+let gSecMan = Cc["@mozilla.org/scriptsecuritymanager;1"].
+              getService(Ci.nsIScriptSecurityManager);
+
 let gFaviconService = Cc["@mozilla.org/browser/favicon-service;1"].
                       getService(Ci.nsIFaviconService);
 
 let gPlacesDatabase = Cc["@mozilla.org/browser/nav-history-service;1"].
                       getService(Ci.nsPIPlacesDatabase).
                       DBConnection.
                       clone(true);
 
 let gSitesStmt = gPlacesDatabase.createAsyncStatement(
-                  "SELECT get_unreversed_host(rev_host) AS host " +
+                  "SELECT url " +
                   "FROM moz_places " +
                   "WHERE rev_host > '.' " +
                   "AND visit_count > 0 " +
                   "GROUP BY rev_host " +
                   "ORDER BY MAX(frecency) DESC " +
                   "LIMIT :limit");
 
 let gVisitStmt = gPlacesDatabase.createAsyncStatement(
@@ -41,24 +44,21 @@ let gVisitStmt = gPlacesDatabase.createA
 
 /**
  * Permission types that should be tested with testExactPermission, as opposed
  * to testPermission. This is based on what consumers use to test these permissions.
  */
 let TEST_EXACT_PERM_TYPES = ["geo", "camera", "microphone"];
 
 /**
- * Site object represents a single site, uniquely identified by a host.
+ * Site object represents a single site, uniquely identified by a principal.
  */
-function Site(host) {
-  this.host = host;
+function Site(principal) {
+  this.principal = principal;
   this.listitem = null;
-
-  this.httpURI = NetUtil.newURI("http://" + this.host);
-  this.httpsURI = NetUtil.newURI("https://" + this.host);
 }
 
 Site.prototype = {
   /**
    * Gets the favicon to use for the site. The callback only gets called if
    * a favicon is found for either the http URI or the https URI.
    *
    * @param aCallback
@@ -70,38 +70,34 @@ Site.prototype = {
         // Use getFaviconLinkForIcon to get image data from the database instead
         // of using the favicon URI to fetch image data over the network.
         aCallback(gFaviconService.getFaviconLinkForIcon(aFaviconURI).spec);
       } catch (e) {
         Cu.reportError("AboutPermissions: " + e);
       }
     }
 
-    // Try to find favicon for both URIs, but always prefer the https favicon.
-    gFaviconService.getFaviconURLForPage(this.httpsURI, function (aURI) {
+    // Get the favicon for the origin
+    gFaviconService.getFaviconURLForPage(this.principal.URI, function (aURI) {
       if (aURI) {
         invokeCallback(aURI);
-      } else {
-        gFaviconService.getFaviconURLForPage(this.httpURI, function (aURI) {
-          if (aURI) {
-            invokeCallback(aURI);
-          }
-        });
       }
     }.bind(this));
   },
 
   /**
    * Gets the number of history visits for the site.
    *
    * @param aCallback
    *        A function that takes the visit count (a number) as a parameter.
    */
   getVisitCount: function Site_getVisitCount(aCallback) {
-    let rev_host = this.host.split("").reverse().join("") + ".";
+    // XXX This won't be a very reliable system, as it will count both http: and https: visits
+    // Unfortunately, I don't think that there is a much better way to do it right now.
+    let rev_host = this.principal.URI.host.split("").reverse().join("") + ".";
     gVisitStmt.params.rev_host = rev_host;
     gVisitStmt.executeAsync({
       handleResult: function(aResults) {
         let row = aResults.getNextRow();
         let count = row.getResultByName("count") || 0;
         try {
           aCallback(count);
         } catch (e) {
@@ -134,19 +130,19 @@ Site.prototype = {
       aResultObj.value =  this.loginSavingEnabled ?
                           Ci.nsIPermissionManager.ALLOW_ACTION :
                           Ci.nsIPermissionManager.DENY_ACTION;
       return true;
     }
 
     let permissionValue;
     if (TEST_EXACT_PERM_TYPES.indexOf(aType) == -1) {
-      permissionValue = Services.perms.testPermission(this.httpURI, aType);
+      permissionValue = Services.perms.testPermissionFromPrincipal(this.principal, aType);
     } else {
-      permissionValue = Services.perms.testExactPermission(this.httpURI, aType);
+      permissionValue = Services.perms.testExactPermissionFromPrincipal(this.principal, aType);
     }
     aResultObj.value = permissionValue;
 
     return permissionValue != Ci.nsIPermissionManager.UNKNOWN_ACTION;
   },
 
   /**
    * Sets a permission for the site given a permission type and value.
@@ -161,46 +157,45 @@ Site.prototype = {
   setPermission: function Site_setPermission(aType, aPerm) {
     // Password saving isn't a nsIPermissionManager permission type, so handle
     // it seperately.
     if (aType == "password") {
       this.loginSavingEnabled = aPerm == Ci.nsIPermissionManager.ALLOW_ACTION;
       return;
     }
 
-    // Using httpURI is kind of bogus, but the permission manager stores the
-    // permission for the host, so the right thing happens in the end.
-    Services.perms.add(this.httpURI, aType, aPerm);
+    Services.perms.addFromPrincipal(this.principal, aType, aPerm);
   },
 
   /**
    * Clears a user-set permission value for the site given a permission type.
    *
    * @param aType
    *        The permission type string stored in permission manager.
    *        e.g. "cookie", "geo", "indexedDB", "popup", "image"
    */
   clearPermission: function Site_clearPermission(aType) {
-    Services.perms.remove(this.httpURI, aType);
+    Services.perms.removeFromPrincipal(this.principal, aType);
   },
 
   /**
    * Gets cookies stored for the site. This does not return cookies stored
    * for the base domain, only the exact hostname stored for the site.
    *
    * @return An array of the cookies set for the site.
    */
   get cookies() {
+    let host = this.principal.URI.host;
     let cookies = [];
-    let enumerator = Services.cookies.getCookiesFromHost(this.host);
+    let enumerator = Services.cookies.getCookiesFromHost(host);
     while (enumerator.hasMoreElements()) {
       let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
       // getCookiesFromHost returns cookies for base domain, but we only want
       // the cookies for the exact domain.
-      if (cookie.rawHost == this.host) {
+      if (cookie.rawHost == host) {
         cookies.push(cookie);
       }
     }
     return cookies;
   },
 
   /**
    * Removes a set of specific cookies from the browser.
@@ -212,37 +207,37 @@ Site.prototype = {
   },
 
   /**
    * Gets logins stored for the site.
    *
    * @return An array of the logins stored for the site.
    */
   get logins() {
-    let httpLogins = Services.logins.findLogins({}, this.httpURI.prePath, "", "");
-    let httpsLogins = Services.logins.findLogins({}, this.httpsURI.prePath, "", "");
-    return httpLogins.concat(httpsLogins);
+    let logins = Services.logins.findLogins({}, this.principal.originNoSuffix, "", "");
+    return logins;
   },
 
   get loginSavingEnabled() {
-    // Only say that login saving is blocked if it is blocked for both http and https.
-    return Services.logins.getLoginSavingEnabled(this.httpURI.prePath) &&
-           Services.logins.getLoginSavingEnabled(this.httpsURI.prePath);
+    return Services.logins.getLoginSavingEnabled(this.principal.originNoSuffix);
   },
 
   set loginSavingEnabled(isEnabled) {
-    Services.logins.setLoginSavingEnabled(this.httpURI.prePath, isEnabled);
-    Services.logins.setLoginSavingEnabled(this.httpsURI.prePath, isEnabled);
+    Services.logins.setLoginSavingEnabled(this.principal.originNoSuffix, isEnabled);
   },
 
   /**
    * Removes all data from the browser corresponding to the site.
    */
   forgetSite: function Site_forgetSite() {
-    ForgetAboutSite.removeDataFromDomain(this.host);
+    // XXX This removes data for an entire domain, rather than just
+    // an origin. This may produce confusing results, as data will
+    // be cleared for the http:// as well as the https:// domain
+    // if you try to forget the https:// site.
+    ForgetAboutSite.removeDataFromDomain(this.principal.URI.host);
   }
 }
 
 /**
  * PermissionDefaults object keeps track of default permissions for sites based
  * on global preferences.
  *
  * Inspired by pageinfo/permissions.js
@@ -361,17 +356,17 @@ let AboutPermissions = {
 
   /**
    * When adding sites to the dom sites-list, divide workload into intervals.
    */
   LIST_BUILD_CHUNK: 5, // interval size
   LIST_BUILD_DELAY: 100, // delay between intervals
 
   /**
-   * Stores a mapping of host strings to Site objects.
+   * Stores a mapping of origin strings to Site objects.
    */
   _sites: {},
 
   sitesList: null,
   _selectedSite: null,
 
   /**
    * For testing, track initializations so we can send notifications
@@ -467,19 +462,19 @@ let AboutPermissions = {
         // aSubject is null when nsIPermisionManager::removeAll() is called.
         if (!aSubject) {
           this._supportedPermissions.forEach(function(aType){
             this.updatePermission(aType);
           }, this);
           break;
         }
         let permission = aSubject.QueryInterface(Ci.nsIPermission);
-        // We can't compare selectedSite.host and permission.host here because
-        // we need to handle the case where a parent domain was changed in a
-        // way that affects the subdomain.
+        // We can't compare selectedSite.principal and permission.principal here
+        // because we need to handle the case where a parent domain was changed
+        // in a way that affects the subdomain.
         if (this._supportedPermissions.indexOf(permission.type) != -1) {
           this.updatePermission(permission.type);
         }
         break;
       case "nsPref:changed":
         this._supportedPermissions.forEach(function(aType){
           this.updatePermission(aType);
         }, this);
@@ -507,18 +502,21 @@ let AboutPermissions = {
    */
   getSitesFromPlaces: function() {
     gSitesStmt.params.limit = this.PLACES_SITES_LIMIT;
     gSitesStmt.executeAsync({
       handleResult: function(aResults) {
         AboutPermissions.startSitesListBatch();
         let row;
         while (row = aResults.getNextRow()) {
-          let host = row.getResultByName("host");
-          AboutPermissions.addHost(host);
+          let spec = row.getResultByName("url");
+          let uri = NetUtil.newURI(spec);
+          let principal = gSecMan.getNoAppCodebasePrincipal(uri);
+
+          AboutPermissions.addPrincipal(principal);
         }
         AboutPermissions.endSitesListBatch();
       },
       handleError: function(aError) {
         Cu.reportError("AboutPermissions: " + aError);
       },
       handleCompletion: function(aReason) {
         // Notify oberservers for testing purposes.
@@ -557,88 +555,90 @@ let AboutPermissions = {
     let logins = Services.logins.getAllLogins();
     logins.forEach(function(aLogin) {
       if (itemCnt % this.LIST_BUILD_CHUNK == 0) {
         yield true;
       }
       try {
         // aLogin.hostname is a string in origin URL format (e.g. "http://foo.com")
         let uri = NetUtil.newURI(aLogin.hostname);
-        this.addHost(uri.host);
+        let principal = gSecMan.getNoAppCodebasePrincipal(uri);
+        this.addPrincipal(principal);
       } catch (e) {
         // newURI will throw for add-ons logins stored in chrome:// URIs
       }
       itemCnt++;
     }, this);
 
     let disabledHosts = Services.logins.getAllDisabledHosts();
     disabledHosts.forEach(function(aHostname) {
       if (itemCnt % this.LIST_BUILD_CHUNK == 0) {
         yield true;
       }
       try {
         // aHostname is a string in origin URL format (e.g. "http://foo.com")
         let uri = NetUtil.newURI(aHostname);
-        this.addHost(uri.host);
+        let principal = gSecMan.getNoAppCodebasePrincipal(uri);
+        this.addPrincipal(principal);
       } catch (e) {
         // newURI will throw for add-ons logins stored in chrome:// URIs
       }
       itemCnt++;
     }, this);
 
     let enumerator = Services.perms.enumerator;
     while (enumerator.hasMoreElements()) {
       if (itemCnt % this.LIST_BUILD_CHUNK == 0) {
         yield true;
       }
       let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
       // Only include sites with exceptions set for supported permission types.
       if (this._supportedPermissions.indexOf(permission.type) != -1) {
-        this.addHost(permission.host);
+        this.addPrincipal(permission.principal);
       }
       itemCnt++;
     }
 
     yield false;
   },
 
   /**
    * Creates a new Site and adds it to _sites if it's not already there.
    *
-   * @param aHost
-   *        A host string.
+   * @param aPrincipal
+   *        A principal.
    */
-  addHost: function(aHost) {
-    if (aHost in this._sites) {
+  addPrincipal: function(aPrincipal) {
+    if (aPrincipal.origin in this._sites) {
       return;
     }
-    let site = new Site(aHost);
-    this._sites[aHost] = site;
+    let site = new Site(aPrincipal);
+    this._sites[aPrincipal.origin] = site;
     this.addToSitesList(site);
   },
 
   /**
    * Populates sites-list richlistbox with data from Site object.
    *
    * @param aSite
    *        A Site object.
    */
   addToSitesList: function(aSite) {
     let item = document.createElement("richlistitem");
     item.setAttribute("class", "site");
-    item.setAttribute("value", aSite.host);
+    item.setAttribute("value", aSite.principal.origin);
 
     aSite.getFavicon(function(aURL) {
       item.setAttribute("favicon", aURL);
     });
     aSite.listitem = item;
 
     // Make sure to only display relevant items when list is filtered
     let filterValue = document.getElementById("sites-filter").value.toLowerCase();
-    item.collapsed = aSite.host.toLowerCase().indexOf(filterValue) == -1;
+    item.collapsed = aSite.principal.origin.toLowerCase().indexOf(filterValue) == -1;
 
     (this._listFragment || this.sitesList).appendChild(item);
   },
 
   startSitesListBatch: function () {
     if (!this._listFragment)
       this._listFragment = document.createDocumentFragment();
   },
@@ -681,44 +681,44 @@ let AboutPermissions = {
   /**
    * Deletes sites for a host and all of its sub-domains. Removes these sites
    * from _sites and removes their corresponding elements from the DOM.
    *
    * @param aHost
    *        The host string corresponding to the site to delete.
    */
   deleteFromSitesList: function(aHost) {
-    for (let host in this._sites) {
-      let site = this._sites[host];
-      if (site.host.hasRootDomain(aHost)) {
+    for (let origin in this._sites) {
+      let site = this._sites[origin];
+      if (site.principal.URI.host.hasRootDomain(aHost)) {
         if (site == this._selectedSite) {
           // Replace site-specific interface with "All Sites" interface.
           this.sitesList.selectedItem = document.getElementById("all-sites-item");
         }
 
         this.sitesList.removeChild(site.listitem);
-        delete this._sites[site.host];
+        delete this._sites[site.principal.origin];
       }
     }
   },
 
   /**
    * Shows interface for managing site-specific permissions.
    */
   onSitesListSelect: function(event) {
     if (event.target.selectedItem.id == "all-sites-item") {
       // Clear the header label value from the previously selected site.
       document.getElementById("site-label").value = "";
       this.manageDefaultPermissions();
       return;
     }
 
-    let host = event.target.value;
-    let site = this._selectedSite = this._sites[host];
-    document.getElementById("site-label").value = host;
+    let origin = event.target.value;
+    let site = this._selectedSite = this._sites[origin];
+    document.getElementById("site-label").value = origin;
     document.getElementById("header-deck").selectedPanel =
       document.getElementById("site-header");
 
     this.updateVisitCount();
     this.updatePermissionsBox();
   },
 
   /**
@@ -763,19 +763,19 @@ let AboutPermissions = {
                       this._noGlobalDeny.indexOf(aType) != -1;
 
     let permissionMenulist = document.getElementById(aType + "-menulist");
     let permissionValue;
     if (!this._selectedSite) {
       // If there is no selected site, we are updating the default permissions interface.
       permissionValue = PermissionDefaults[aType];
       if (aType == "cookie")
-	// cookie-9 corresponds to ALLOW_FIRST_PARTY_ONLY, which is reserved
-	// for site-specific preferences only.
-	document.getElementById("cookie-9").hidden = true;
+	      // cookie-9 corresponds to ALLOW_FIRST_PARTY_ONLY, which is reserved
+	      // for site-specific preferences only.
+	      document.getElementById("cookie-9").hidden = true;
     } else {
       if (aType == "cookie")
         document.getElementById("cookie-9").hidden = false;
       let result = {};
       permissionValue = this._selectedSite.getPermission(aType, result) ?
                         result.value : PermissionDefaults[aType];
     }
 
@@ -820,28 +820,28 @@ let AboutPermissions = {
     document.getElementById("passwords-manage-all-button").hidden = true;
     document.getElementById("passwords-count").hidden = false;
   },
 
   /**
    * Opens password manager dialog.
    */
   managePasswords: function() {
-    let selectedHost = "";
+    let selectedOrigin = "";
     if (this._selectedSite) {
-      selectedHost = this._selectedSite.host;
+      selectedOrigin = this._selectedSite.principal.URI.prePath;
     }
 
     let win = Services.wm.getMostRecentWindow("Toolkit:PasswordManager");
     if (win) {
-      win.setFilter(selectedHost);
+      win.setFilter(selectedOrigin);
       win.focus();
     } else {
       window.openDialog("chrome://passwordmgr/content/passwordManager.xul",
-                        "Toolkit:PasswordManager", "", {filterString : selectedHost});
+                        "Toolkit:PasswordManager", "", {filterString : selectedOrigin});
     }
   },
 
   updateCookiesCount: function() {
     if (!this._selectedSite) {
       document.getElementById("cookies-count").hidden = true;
       document.getElementById("cookies-clear-all-button").hidden = false;
       document.getElementById("cookies-manage-all-button").hidden = false;
@@ -872,19 +872,21 @@ let AboutPermissions = {
     site.clearCookies(site.cookies);
     this.updateCookiesCount();
   },
 
   /**
    * Opens cookie manager dialog.
    */
   manageCookies: function() {
+    // Cookies are stored by-host, and thus we filter the cookie window
+    // using only the host of the selected principal's origin
     let selectedHost = "";
     if (this._selectedSite) {
-      selectedHost = this._selectedSite.host;
+      selectedHost = this._selectedSite.principal.URI.host;
     }
 
     let win = Services.wm.getMostRecentWindow("Browser:Cookies");
     if (win) {
       win.gCookiesWindow.setFilter(selectedHost);
       win.focus();
     } else {
       window.openDialog("chrome://browser/content/preferences/cookies.xul",
--- a/browser/components/preferences/advanced.js
+++ b/browser/components/preferences/advanced.js
@@ -4,16 +4,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 // Load DownloadUtils module for convertByteUnits
 Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
 Components.utils.import("resource://gre/modules/ctypes.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/LoadContextInfo.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
 
 var gAdvancedPane = {
   _inited: false,
 
   /**
    * Brings the appropriate tab to the front and initializes various bits of UI.
    */
   init: function ()
@@ -466,17 +467,17 @@ var gAdvancedPane = {
       groups = cacheService.getGroups();
 
     var ios = Components.classes["@mozilla.org/network/io-service;1"].
               getService(Components.interfaces.nsIIOService);
 
     var usage = 0;
     for (var i = 0; i < groups.length; i++) {
       var uri = ios.newURI(groups[i], null, null);
-      if (uri.asciiHost == perm.host) {
+      if (perm.matchesURI(uri, true)) {
         var cache = cacheService.getActiveCache(groups[i]);
         usage += cache.usage;
       }
     }
 
     return usage;
   },
 
@@ -503,17 +504,17 @@ var gAdvancedPane = {
     while (enumerator.hasMoreElements()) {
       var perm = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission);
       if (perm.type == "offline-app" &&
           perm.capability != Components.interfaces.nsIPermissionManager.DEFAULT_ACTION &&
           perm.capability != Components.interfaces.nsIPermissionManager.DENY_ACTION) {
         var row = document.createElement("listitem");
         row.id = "";
         row.className = "offlineapp";
-        row.setAttribute("host", perm.host);
+        row.setAttribute("origin", perm.principal.origin);
         var converted = DownloadUtils.
                         convertByteUnits(this._getOfflineAppUsage(perm, groups));
         row.setAttribute("usage",
                          bundle.getFormattedString("offlineAppUsage",
                                                    converted));
         list.appendChild(row);
       }
     }
@@ -529,58 +530,53 @@ var gAdvancedPane = {
       removeButton.setAttribute("disabled", "true");
     }
   },
 
   removeOfflineApp: function()
   {
     var list = document.getElementById("offlineAppsList");
     var item = list.selectedItem;
-    var host = item.getAttribute("host");
+    var origin = item.getAttribute("origin");
+    var principal = BrowserUtils.principalFromOrigin(origin);
 
     var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
                             .getService(Components.interfaces.nsIPromptService);
     var flags = prompts.BUTTON_TITLE_IS_STRING * prompts.BUTTON_POS_0 +
                 prompts.BUTTON_TITLE_CANCEL * prompts.BUTTON_POS_1;
 
     var bundle = document.getElementById("bundlePreferences");
     var title = bundle.getString("offlineAppRemoveTitle");
-    var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [host]);
+    var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [principal.URI.prePath]);
     var confirm = bundle.getString("offlineAppRemoveConfirm");
     var result = prompts.confirmEx(window, title, prompt, flags, confirm,
                                    null, null, null, {});
     if (result != 0)
       return;
 
+    // get the permission
+    var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                       .getService(Components.interfaces.nsIPermissionManager);
+    var perm = pm.getPermissionObject(principal, "offline-app");
+
     // clear offline cache entries
     var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
                        getService(Components.interfaces.nsIApplicationCacheService);
     var ios = Components.classes["@mozilla.org/network/io-service;1"].
               getService(Components.interfaces.nsIIOService);
     var groups = cacheService.getGroups();
     for (var i = 0; i < groups.length; i++) {
-        let uri = ios.newURI(groups[i], null, null);
-        if (uri.asciiHost == host) {
+        var uri = ios.newURI(groups[i], null, null);
+        if (perm.matchesURI(uri, true)) {
             var cache = cacheService.getActiveCache(groups[i]);
             cache.discard();
         }
     }
 
-    // remove the permission
-    var pm = Components.classes["@mozilla.org/permissionmanager;1"]
-                       .getService(Components.interfaces.nsIPermissionManager);
-    let uri;
-    try {
-      // file:// URIs are stored with their scheme. We try to parse them first, as
-      // URIs like http://file:///foo/bar/baz.html will parse as HTTP URIs.
-      uri = ios.newURI(host, null, null);
-    } catch (e) {
-      uri = ios.newURI("http://" + host, null, null);
-    }
-    pm.remove(uri, "offline-app");
+    pm.removePermission(perm);
 
     list.removeChild(item);
     gAdvancedPane.offlineAppSelected();
     this.updateActualAppCacheSize();
   },
 
   // UPDATE TAB
 
--- a/browser/components/preferences/in-content/advanced.js
+++ b/browser/components/preferences/in-content/advanced.js
@@ -497,17 +497,17 @@ var gAdvancedPane = {
     var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
                        getService(Components.interfaces.nsIApplicationCacheService);
     var ios = Components.classes["@mozilla.org/network/io-service;1"].
               getService(Components.interfaces.nsIIOService);
 
     var usage = 0;
     for (var i = 0; i < groups.length; i++) {
       var uri = ios.newURI(groups[i], null, null);
-      if (uri.asciiHost == perm.host) {
+      if (perm.matchesURI(uri, true)) {
         var cache = cacheService.getActiveCache(groups[i]);
         usage += cache.usage;
       }
     }
 
     return usage;
   },
 
@@ -539,17 +539,17 @@ var gAdvancedPane = {
     while (enumerator.hasMoreElements()) {
       var perm = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission);
       if (perm.type == "offline-app" &&
           perm.capability != Components.interfaces.nsIPermissionManager.DEFAULT_ACTION &&
           perm.capability != Components.interfaces.nsIPermissionManager.DENY_ACTION) {
         var row = document.createElement("listitem");
         row.id = "";
         row.className = "offlineapp";
-        row.setAttribute("host", perm.host);
+        row.setAttribute("origin", perm.principal.origin);
         var converted = DownloadUtils.
                         convertByteUnits(this._getOfflineAppUsage(perm, groups));
         row.setAttribute("usage",
                          bundle.getFormattedString("offlineAppUsage",
                                                    converted));
         list.appendChild(row);
       }
     }
@@ -565,60 +565,55 @@ var gAdvancedPane = {
       removeButton.setAttribute("disabled", "true");
     }
   },
 
   removeOfflineApp: function()
   {
     var list = document.getElementById("offlineAppsList");
     var item = list.selectedItem;
-    var host = item.getAttribute("host");
+    var origin = item.getAttribute("origin");
+    var principal = BrowserUtils.principalFromOrigin(origin);
 
     var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
                             .getService(Components.interfaces.nsIPromptService);
     var flags = prompts.BUTTON_TITLE_IS_STRING * prompts.BUTTON_POS_0 +
                 prompts.BUTTON_TITLE_CANCEL * prompts.BUTTON_POS_1;
 
     var bundle = document.getElementById("bundlePreferences");
     var title = bundle.getString("offlineAppRemoveTitle");
-    var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [host]);
+    var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [principal.URI.prePath]);
     var confirm = bundle.getString("offlineAppRemoveConfirm");
     var result = prompts.confirmEx(window, title, prompt, flags, confirm,
                                    null, null, null, {});
     if (result != 0)
       return;
 
+    // get the permission
+    var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                       .getService(Components.interfaces.nsIPermissionManager);
+    var perm = pm.getPermissionObject(principal, "offline-app");
+
     // clear offline cache entries
     try {
       var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
                          getService(Components.interfaces.nsIApplicationCacheService);
       var ios = Components.classes["@mozilla.org/network/io-service;1"].
                 getService(Components.interfaces.nsIIOService);
       var groups = cacheService.getGroups();
       for (var i = 0; i < groups.length; i++) {
-          let uri = ios.newURI(groups[i], null, null);
-          if (uri.asciiHost == host) {
+          var uri = ios.newURI(groups[i], null, null);
+          if (perm.matchesURI(uri, true)) {
               var cache = cacheService.getActiveCache(groups[i]);
               cache.discard();
           }
       }
     } catch (e) {}
 
-    // remove the permission
-    var pm = Components.classes["@mozilla.org/permissionmanager;1"]
-                       .getService(Components.interfaces.nsIPermissionManager);
-    let uri;
-    try {
-      // file:// URIs are stored with their scheme. We try to parse them first, as
-      // URIs like http://file:///foo/bar/baz.html will parse as HTTP URIs.
-      uri = ios.newURI(host, null, null);
-    } catch (e) {
-      uri = ios.newURI("http://" + host, null, null);
-    }
-    pm.remove(uri, "offline-app");
+    pm.removePermission(perm);
 
     list.removeChild(item);
     gAdvancedPane.offlineAppSelected();
     this.updateActualAppCacheSize();
   },
 
   // UPDATE TAB
 
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -364,29 +364,16 @@ var gPrivacyPane = {
       mode.selectedIndex = this._lastMode;
       mode.doCommand();
 
       this._shouldPromptForRestart = true;
   },
 
   // HISTORY
 
-  /**
-   * Update browser.urlbar.autocomplete.enabled when a
-   * browser.urlbar.suggest.* pref is changed from the ui.
-   */
-  writeSuggestionPref() {
-    let getVal = (aPref) => {
-      return document.getElementById("browser.urlbar.suggest." + aPref).value;
-    }
-    // autocomplete.enabled is true if any of the suggestions is true
-    let enabled = ["history", "bookmark", "openpage", "searches"].map(getVal).some(v => v);
-    Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", enabled);
-  },
-
   /*
    * Preferences:
    *
    * places.history.enabled
    * - whether history is enabled or not
    * browser.formfill.enable
    * - true if entries in forms and the search bar should be saved, false
    *   otherwise
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -248,25 +248,21 @@
 
 <!-- Location Bar -->
 <groupbox id="locationBarGroup"
           data-category="panePrivacy"
           hidden="true">
   <caption><label>&locationBar.label;</label></caption>
   <label id="locationBarSuggestionLabel">&locbar.suggest.label;</label>
   <checkbox id="historySuggestion" label="&locbar.history.label;"
-            onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
             accesskey="&locbar.history.accesskey;"
             preference="browser.urlbar.suggest.history"/>
   <checkbox id="bookmarkSuggestion" label="&locbar.bookmarks.label;"
-            onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
             accesskey="&locbar.bookmarks.accesskey;"
             preference="browser.urlbar.suggest.bookmark"/>
   <checkbox id="openpageSuggestion" label="&locbar.openpage.label;"
-            onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
             accesskey="&locbar.openpage.accesskey;"
             preference="browser.urlbar.suggest.openpage"/>
   <checkbox id="searchesSuggestion" label="&locbar.searches.label;"
             hidden="true"
-            onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
             accesskey="&locbar.searches.accesskey;"
             preference="browser.urlbar.suggest.searches"/>
 </groupbox>
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -182,25 +182,25 @@ let gSyncPane = {
     });
     setEventListener("syncEnginesList", "select", function () {
       if (this.selectedCount)
         this.clearSelection();
     });
     setEventListener("syncComputerName", "change", function (e) {
       gSyncUtils.changeName(e.target);
     });
-    setEventListener("fxaChangeDeviceName", "click", function () {
+    setEventListener("fxaChangeDeviceName", "command", function () {
       this._toggleComputerNameControls(true);
       this._focusComputerNameTextbox();
     });
-    setEventListener("fxaCancelChangeDeviceName", "click", function () {
+    setEventListener("fxaCancelChangeDeviceName", "command", function () {
       this._toggleComputerNameControls(false);
       this._updateComputerNameValue(false);
     });
-    setEventListener("fxaSaveChangeDeviceName", "click", function () {
+    setEventListener("fxaSaveChangeDeviceName", "command", function () {
       this._toggleComputerNameControls(false);
       this._updateComputerNameValue(true);
     });
     setEventListener("unlinkDevice", "click", function () {
       gSyncPane.startOver(true);
       return false;
     });
     setEventListener("tosPP-normal-ToS", "click", gSyncPane.openToS);
@@ -314,44 +314,45 @@ let gSyncPane = {
         if (!data) {
           this.page = FXA_PAGE_LOGGED_OUT;
           return false;
         }
         this.page = FXA_PAGE_LOGGED_IN;
         // We are logged in locally, but maybe we are in a state where the
         // server rejected our credentials (eg, password changed on the server)
         let fxaLoginStatus = document.getElementById("fxaLoginStatus");
-        let enginesListDisabled;
+        let syncReady;
         // Not Verfied implies login error state, so check that first.
         if (!data.verified) {
           fxaLoginStatus.selectedIndex = FXA_LOGIN_UNVERIFIED;
-          enginesListDisabled = true;
+          syncReady = false;
         // So we think we are logged in, so login problems are next.
         // (Although if the Sync identity manager is still initializing, we
         // ignore login errors and assume all will eventually be good.)
         // LOGIN_FAILED_LOGIN_REJECTED explicitly means "you must log back in".
         // All other login failures are assumed to be transient and should go
         // away by themselves, so aren't reflected here.
         } else if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED) {
           fxaLoginStatus.selectedIndex = FXA_LOGIN_FAILED;
-          enginesListDisabled = true;
+          syncReady = false;
         // Else we must be golden (or in an error state we expect to magically
         // resolve itself)
         } else {
           fxaLoginStatus.selectedIndex = FXA_LOGIN_VERIFIED;
-          enginesListDisabled = false;
+          syncReady = true;
         }
         fxaEmailAddress1Label.textContent = data.email;
         document.getElementById("fxaEmailAddress2").textContent = data.email;
         document.getElementById("fxaEmailAddress3").textContent = data.email;
         document.getElementById("fxaSyncComputerName").value = Weave.Service.clientsEngine.localName;
         let engines = document.getElementById("fxaSyncEngines")
         for (let checkbox of engines.querySelectorAll("checkbox")) {
-          checkbox.disabled = enginesListDisabled;
+          checkbox.disabled = !syncReady;
         }
+        document.getElementById("fxaChangeDeviceName").disabled = !syncReady;
 
         // Clear the profile image (if any) of the previously logged in account.
         document.getElementById("fxaProfileImage").style.removeProperty("background-image");
 
         // If the account is verified the next promise in the chain will
         // fetch profile data.
         return data.verified;
       }).then(isVerified => {
--- a/browser/components/preferences/in-content/sync.xul
+++ b/browser/components/preferences/in-content/sync.xul
@@ -340,18 +340,18 @@
             <button id="fxaSaveChangeDeviceName"
                     label="&saveChangeSyncDeviceName.label;"
                     hidden="true"/>
           </hbox>
         </hbox>
       </vbox>
     </groupbox>
     <spacer flex="1"/>
-    <hbox id="tosPP-small">
+    <vbox 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>
-    </hbox>
+    </vbox>
   </vbox>
 </deck>
--- a/browser/components/preferences/permissions.js
+++ b/browser/components/preferences/permissions.js
@@ -4,20 +4,20 @@
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
 const nsICookiePermission = Components.interfaces.nsICookiePermission;
 
 const NOTIFICATION_FLUSH_PERMISSIONS = "flush-pending-permissions";
 
-function Permission(host, rawHost, type, capability)
+function Permission(principal, type, capability)
 {
-  this.host = host;
-  this.rawHost = rawHost;
+  this.principal = principal;
+  this.origin = principal.origin;
   this.type = type;
   this.capability = capability;
 }
 
 var gPermissionManager = {
   _type                 : "",
   _permissions          : [],
   _permissionsToAdd     : new Map(),
@@ -30,17 +30,17 @@ var gPermissionManager = {
     _rowCount: 0,
     get rowCount()
     {
       return this._rowCount;
     },
     getCellText: function (aRow, aColumn)
     {
       if (aColumn.id == "siteCol")
-        return gPermissionManager._permissions[aRow].rawHost;
+        return gPermissionManager._permissions[aRow].origin;
       else if (aColumn.id == "statusCol")
         return gPermissionManager._permissions[aRow].capability;
       return "";
     },
 
     isSeparator: function(aIndex) { return false; },
     isSorted: function() { return false; },
     isContainer: function(aIndex) { return false; },
@@ -77,74 +77,81 @@ var gPermissionManager = {
       break;
     }
     return this._bundle.getString(stringKey);
   },
 
   addPermission: function (aCapability)
   {
     var textbox = document.getElementById("url");
-    var host = textbox.value.replace(/^\s*([-\w]*:\/+)?/, ""); // trim any leading space and scheme
+    var input_url = textbox.value.replace(/^\s*/, ""); // trim any leading space
+    let principal;
     try {
-      var uri = Services.io.newURI("http://"+host, null, null);
-      host = uri.host;
+      // If the uri doesn't successfully parse, try adding a http:// and parsing again
+      let uri;
+      try {
+        let uri = Services.io.newURI(input_url, null, null);
+      } catch(ex) {
+        uri = Services.io.newURI("http://" + input_url, null, null);
+      }
+      principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
     } catch(ex) {
       var message = this._bundle.getString("invalidURI");
       var title = this._bundle.getString("invalidURITitle");
       Services.prompt.alert(window, title, message);
       return;
     }
 
     var capabilityString = this._getCapabilityString(aCapability);
 
     // check whether the permission already exists, if not, add it
-    let hostExists = false;
+    let permissionExists = false;
     let capabilityExists = false;
     for (var i = 0; i < this._permissions.length; ++i) {
-      if (this._permissions[i].rawHost == host) {
-        hostExists = true;
+      if (this._permissions[i].principal.equals(principal)) {
+        permissionExists = true;
         capabilityExists = this._permissions[i].capability == capabilityString;
         if (!capabilityExists) {
           this._permissions[i].capability = capabilityString;
         }
         break;
       }
     }
 
-    let permissionParams = {host: host, type: this._type, capability: aCapability};
-    if (!hostExists) {
-      this._permissionsToAdd.set(host, permissionParams);
+    let permissionParams = {principal: principal, type: this._type, capability: aCapability};
+    if (!permissionExists) {
+      this._permissionsToAdd.set(principal.origin, permissionParams);
       this._addPermission(permissionParams);
     }
     else if (!capabilityExists) {
-        this._permissionsToAdd.set(host, permissionParams);
-        this._handleCapabilityChange();
+      this._permissionsToAdd.set(principal.origin, permissionParams);
+      this._handleCapabilityChange();
     }
 
     textbox.value = "";
     textbox.focus();
 
     // covers a case where the site exists already, so the buttons don't disable
     this.onHostInput(textbox);
 
     // enable "remove all" button as needed
     document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
   },
 
   _removePermission: function(aPermission)
   {
-    this._removePermissionFromList(aPermission.host);
+    this._removePermissionFromList(aPermission.principal);
 
     // If this permission was added during this session, let's remove
     // it from the pending adds list to prevent calls to the
     // permission manager.
-    let isNewPermission = this._permissionsToAdd.delete(aPermission.host);
+    let isNewPermission = this._permissionsToAdd.delete(aPermission.principal.origin);
 
     if (!isNewPermission) {
-      this._permissionsToDelete.set(aPermission.host, aPermission);
+      this._permissionsToDelete.set(aPermission.principal.origin, aPermission);
     }
 
   },
 
   _handleCapabilityChange: function ()
   {
     // Re-do the sort, if the status changed from Block to Allow
     // or vice versa, since if we're sorted on status, we may no
@@ -271,17 +278,17 @@ var gPermissionManager = {
       if (permission.type != this._type)
         return;
 
       if (aData == "added") {
         this._addPermission(permission);
       }
       else if (aData == "changed") {
         for (var i = 0; i < this._permissions.length; ++i) {
-          if (this._permissions[i].host == permission.host) {
+          if (permission.matches(this._permissions[i].principal, true)) {
             this._permissions[i].capability = this._getCapabilityString(permission.capability);
             break;
           }
         }
         this._handleCapabilityChange();
       }
       else if (aData == "deleted") {
         this._removePermissionFromList(permission);
@@ -358,23 +365,21 @@ var gPermissionManager = {
   onApplyChanges: function()
   {
     // Stop observing permission changes since we are about
     // to write out the pending adds/deletes and don't need
     // to update the UI
     this.uninit();
 
     for (let permissionParams of this._permissionsToAdd.values()) {
-      let uri = Services.io.newURI("http://" + permissionParams.host, null, null);
-      Services.perms.add(uri, permissionParams.type, permissionParams.capability);
+      Services.perms.addFromPrincipal(permissionParams.principal, permissionParams.type, permissionParams.capability);
     }
 
     for (let p of this._permissionsToDelete.values()) {
-      let uri = Services.io.newURI("http://" + p.host, null, null);
-      Services.perms.remove(uri, p.type);
+      Services.perms.removeFromPrincipal(p.principal, p.type);
     }
 
     window.close();
   },
 
   _loadPermissions: function ()
   {
     this._tree = document.getElementById("permissionsTree");
@@ -387,58 +392,57 @@ var gPermissionManager = {
       var nextPermission = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission);
       this._addPermissionToList(nextPermission);
     }
 
     this._view._rowCount = this._permissions.length;
 
     // sort and display the table
     this._tree.view = this._view;
-    this.onPermissionSort("rawHost");
+    this.onPermissionSort("origin");
 
     // disable "remove all" button if there are none
     document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
   },
 
   _addPermissionToList: function (aPermission)
   {
     if (aPermission.type == this._type &&
         (!this._manageCapability ||
          (aPermission.capability == this._manageCapability))) {
 
-      var host = aPermission.host;
+      var principal = aPermission.principal;
       var capabilityString = this._getCapabilityString(aPermission.capability);
-      var p = new Permission(host,
-                             (host.charAt(0) == ".") ? host.substring(1,host.length) : host,
+      var p = new Permission(principal,
                              aPermission.type,
                              capabilityString);
       this._permissions.push(p);
     }
   },
 
-  _removePermissionFromList: function (aHost)
+  _removePermissionFromList: function (aPrincipal)
   {
     for (let i = 0; i < this._permissions.length; ++i) {
-      if (this._permissions[i].host == aHost) {
+      if (this._permissions[i].principal.equals(aPrincipal)) {
         this._permissions.splice(i, 1);
         this._view._rowCount--;
         this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1);
         this._tree.treeBoxObject.invalidate();
         break;
       }
     }
   },
 
-  setHost: function (aHost)
+  setOrigin: function (aOrigin)
   {
-    document.getElementById("url").value = aHost;
+    document.getElementById("url").value = aOrigin;
   }
 };
 
-function setHost(aHost)
+function setOrigin(aOrigin)
 {
-  gPermissionManager.setHost(aHost);
+  gPermissionManager.setOrigin(aOrigin);
 }
 
 function initWithParams(aParams)
 {
   gPermissionManager.init(aParams);
 }
--- a/browser/components/preferences/privacy.js
+++ b/browser/components/preferences/privacy.js
@@ -319,29 +319,16 @@ var gPrivacyPane = {
       mode.selectedIndex = this._lastMode;
       mode.doCommand();
 
       this._shouldPromptForRestart = true;
   },
 
   // HISTORY
 
-  /**
-   * Update browser.urlbar.autocomplete.enabled when a
-   * browser.urlbar.suggest.* pref is changed from the ui.
-   */
-  writeSuggestionPref() {
-    let getVal = (aPref) => {
-      return document.getElementById("browser.urlbar.suggest." + aPref).value;
-    }
-    // autocomplete.enabled is true if any of the suggestions is true
-    let enabled = ["history", "bookmark", "openpage", "searches"].map(getVal).some(v => v);
-    Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", enabled);
-  },
-
   /*
    * Preferences:
    *
    * places.history.enabled
    * - whether history is enabled or not
    * browser.formfill.enable
    * - true if entries in forms and the search bar should be saved, false
    *   otherwise
--- a/browser/components/preferences/privacy.xul
+++ b/browser/components/preferences/privacy.xul
@@ -267,30 +267,26 @@
     <!-- Location Bar -->
     <groupbox id="locationBarGroup">
       <caption label="&locationBar.label;"/>
 
       <label id="locationBarSuggestionLabel">&locbar.suggest.label;</label>
 
       <vbox id="tabPrefsBox" align="start" flex="1">
         <checkbox id="historySuggestion" label="&locbar.history.label;"
-                  onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
                   accesskey="&locbar.history.accesskey;"
                   preference="browser.urlbar.suggest.history"/>
         <checkbox id="bookmarkSuggestion" label="&locbar.bookmarks.label;"
-                  onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
                   accesskey="&locbar.bookmarks.accesskey;"
                   preference="browser.urlbar.suggest.bookmark"/>
         <checkbox id="openpageSuggestion" label="&locbar.openpage.label;"
-                  onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
                   accesskey="&locbar.openpage.accesskey;"
                   preference="browser.urlbar.suggest.openpage"/>
         <checkbox id="searchesSuggestion" label="&locbar.searches.label;"
                   hidden="true"
-                  onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
                   accesskey="&locbar.searches.accesskey;"
                   preference="browser.urlbar.suggest.searches"/>
       </vbox>
     </groupbox>
 
   </prefpane>
 
 </overlay>
--- a/browser/components/preferences/tests/browser_chunk_permissions.js
+++ b/browser/components/preferences/tests/browser_chunk_permissions.js
@@ -98,40 +98,40 @@ var tests = [
   {
     desc: "test filtering before sites-list is fully constructed.",
     preInit: function() {
       let sitesFilter = gBrowser.contentDocument.getElementById("sites-filter");
       sitesFilter.value = TEST_URI_2.host;
       sitesFilter.doCommand();
     },
     run: function() {
-      let testSite1 = getSiteItem(TEST_URI_1.host);
+      let testSite1 = getSiteItem(TEST_URI_1.prePath);
       ok(testSite1.collapsed, "test site 1 is collapsed after early filtering");
-      let testSite2 = getSiteItem(TEST_URI_2.host);
+      let testSite2 = getSiteItem(TEST_URI_2.prePath);
       ok(!testSite2.collapsed, "test site 2 is not collapsed after early filtering");
-      let testSite3 = getSiteItem(TEST_URI_3.host);
+      let testSite3 = getSiteItem(TEST_URI_3.prePath);
       ok(testSite3.collapsed, "test site 3 is collapsed after early filtering");
 
       runNextTest();
     }
   },
   {
     desc: "test removing from sites-list before it is fully constructed.",
     preInit: function() {
       ForgetAboutSite.removeDataFromDomain(TEST_URI_2.host);
     },
     run: function() {
-      let testSite1 = getSiteItem(TEST_URI_1.host);
+      let testSite1 = getSiteItem(TEST_URI_1.prePath);
       ok(testSite1, "test site 1 was not removed from sites list");
-      let testSite2 = getSiteItem(TEST_URI_2.host);
+      let testSite2 = getSiteItem(TEST_URI_2.prePath);
       ok(!testSite2, "test site 2 was pre-removed from sites list");
-      let testSite3 = getSiteItem(TEST_URI_3.host);
+      let testSite3 = getSiteItem(TEST_URI_3.prePath);
       ok(testSite3, "test site 3 was not removed from sites list");
 
       runNextTest();
     }
   }
 ];
 
-function getSiteItem(aHost) {
+function getSiteItem(aPrePath) {
   return gBrowser.contentDocument.
-                  querySelector(".site[value='" + aHost + "']");
+                  querySelector(".site[value='" + aPrePath + "']");
 }
--- a/browser/components/preferences/tests/browser_cookies_exceptions.js
+++ b/browser/components/preferences/tests/browser_cookies_exceptions.js
@@ -15,58 +15,58 @@ var testRunner = {
         test: function(params) {
           params.url.value = "test.com";
           params.btnAllow.doCommand();
           is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
           is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
                                           "permission text should be set correctly");
           params.btnApplyChanges.doCommand();
         },
-        observances: [{ type: "cookie", host: "test.com", data: "added",
+        observances: [{ type: "cookie", origin: "http://test.com", data: "added",
                         capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
       },
       {
         test: function(params) {
           params.url.value = "test.com";
           params.btnBlock.doCommand();
           is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
                                           "permission should change to deny in UI");
           params.btnApplyChanges.doCommand();
         },
-        observances: [{ type: "cookie", host: "test.com", data: "changed",
+        observances: [{ type: "cookie", origin: "http://test.com", data: "changed",
                         capability: Ci.nsIPermissionManager.DENY_ACTION  }],
       },
       {
         test: function(params) {
           params.url.value = "test.com";
           params.btnAllow.doCommand();
           is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
                                           "permission should revert back to allow");
           params.btnApplyChanges.doCommand();
         },
-        observances: [{ type: "cookie", host: "test.com", data: "changed",
+        observances: [{ type: "cookie", origin: "http://test.com", data: "changed",
                         capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
       },
       {
         test: function(params) {
           params.url.value = "test.com";
           params.btnRemove.doCommand();
           is(params.tree.view.rowCount, 0, "exception should be removed");
           params.btnApplyChanges.doCommand();
         },
-        observances: [{ type: "cookie", host: "test.com", data: "deleted" }],
+        observances: [{ type: "cookie", origin: "http://test.com", data: "deleted" }],
       },
       {
         test: function(params) {
           let uri = params.ioService.newURI("http://test.com", null, null);
           params.pm.add(uri, "popup", Ci.nsIPermissionManager.DENY_ACTION);
           is(params.tree.view.rowCount, 0, "adding unrelated permission should not change display");
           params.btnApplyChanges.doCommand();
         },
-        observances: [{ type: "popup", host: "test.com", data: "added",
+        observances: [{ type: "popup", origin: "http://test.com", data: "added",
                         capability: Ci.nsIPermissionManager.DENY_ACTION }],
         cleanUp: function(params) {
           let uri = params.ioService.newURI("http://test.com", null, null);
           params.pm.remove(uri, "popup");
         },
       },
     ],
 
@@ -155,22 +155,27 @@ var testRunner = {
                 // See bug 1063410.
                 return;
               }
 
               let permission = aSubject.QueryInterface(Ci.nsIPermission);
               let expected = testRunner.tests[testRunner._currentTest].observances.shift();
 
               is(aData, expected.data, "type of message should be the same");
-              for each (let prop in ["type", "host", "capability"]) {
+              for each (let prop in ["type", "capability"]) {
                 if (expected[prop])
                   is(permission[prop], expected[prop],
                     "property: \"" + prop  + "\" should be equal");
               }
 
+              if (expected.origin) {
+                is(permission.principal.origin, expected.origin,
+                   "property: \"origin\" should be equal");
+              }
+
               os.removeObserver(permObserver, "perm-changed");
 
               if (testRunner.tests[testRunner._currentTest].cleanup) {
                 testRunner.tests[testRunner._currentTest].cleanup();
               }
 
               testRunner.dialog.close(params);
               win.close();
--- a/browser/components/preferences/tests/browser_permissions.js
+++ b/browser/components/preferences/tests/browser_permissions.js
@@ -110,32 +110,32 @@ var tests = [
 
     runNextTest();
   },
 
   function test_sites_list() {
     is(gSitesList.firstChild.id, "all-sites-item",
        "all sites is the first item in the sites list");
 
-    ok(getSiteItem(TEST_URI_1.host), "site item from places db exists");
-    ok(getSiteItem(TEST_URI_2.host), "site item from enumerating services exists");
+    ok(getSiteItem(TEST_URI_1.prePath), "site item from places db exists");
+    ok(getSiteItem(TEST_URI_2.prePath), "site item from enumerating services exists");
 
     runNextTest();
   },
 
   function test_filter_sites_list() {
     // set filter to test host
     let sitesFilter = gBrowser.contentDocument.getElementById("sites-filter");
     sitesFilter.value = TEST_URI_1.host;
     sitesFilter.doCommand();
 
     // make sure correct sites are collapsed/showing
-    let testSite1 = getSiteItem(TEST_URI_1.host);
+    let testSite1 = getSiteItem(TEST_URI_1.prePath);
     ok(!testSite1.collapsed, "test site 1 is not collapsed");
-    let testSite2 = getSiteItem(TEST_URI_2.host);
+    let testSite2 = getSiteItem(TEST_URI_2.prePath);
     ok(testSite2.collapsed, "test site 2 is collapsed");
 
     // clear filter
     sitesFilter.value = "";
     sitesFilter.doCommand();
 
     runNextTest();
   },
@@ -197,23 +197,23 @@ var tests = [
   function test_manage_all_cookies() {
     // make sure "Manage All Cookies..." button opens the correct dialog
     addWindowListener("chrome://browser/content/preferences/cookies.xul", runNextTest);
     gBrowser.contentDocument.getElementById("cookies-manage-all-button").doCommand();
   },
 
   function test_select_site() {
     // select the site that has the permissions we set at the beginning of the test
-    let testSiteItem = getSiteItem(TEST_URI_2.host);
+    let testSiteItem = getSiteItem(TEST_URI_2.prePath);
     gSitesList.selectedItem = testSiteItem;
 
     let siteHeader = gBrowser.contentDocument.getElementById("site-header");
     is(siteHeader, gHeaderDeck.selectedPanel,
        "correct header shown for a specific site");
-    is(gSiteLabel.value, TEST_URI_2.host, "header updated for selected site");
+    is(gSiteLabel.value, TEST_URI_2.prePath, "header updated for selected site");
 
     ok(!gBrowser.contentDocument.getElementById("passwords-count").hidden,
        "passwords count is not hidden");
     ok(!gBrowser.contentDocument.getElementById("cookies-count").hidden,
        "cookies count is not hidden");
 
     // Test to make sure "Allow" items are *not* hidden for certain permission types
     NO_GLOBAL_ALLOW.forEach(function(aType) {
@@ -278,17 +278,17 @@ var tests = [
     PlacesTestUtils.clearHistory().then(() => {
       is(gSiteLabel.value, "", "site label cleared");
 
       let allSitesItem = gBrowser.contentDocument.getElementById("all-sites-item");
       is(gSitesList.selectedItem, allSitesItem,
          "all sites item selected after forgetting selected site");
 
       // check to make sure site is gone from sites list
-      let testSiteItem = getSiteItem(TEST_URI_2.host);
+      let testSiteItem = getSiteItem(TEST_URI_2.prePath);
       ok(!testSiteItem, "site removed from sites list");
 
       // check to make sure we forgot all permissions corresponding to site
       for (let type in TEST_PERMS) {
         if (type == "password") {
           ok(Services.logins.getLoginSavingEnabled(TEST_URI_2.prePath),
              "password saving should be enabled by default");
         } else {
--- a/browser/components/preferences/translation.js
+++ b/browser/components/preferences/translation.js
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/BrowserUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gLangBundle", () =>
   Services.strings.createBundle("chrome://global/locale/languageNames.properties"));
 
 const kPermissionType = "translate";
 const kLanguagesPref = "browser.translation.neverForLanguages";
 
 function Tree(aId, aData)
@@ -78,17 +79,17 @@ let gTranslationExceptions = {
     // Load site permissions into an array.
     this._sites = [];
     let enumerator = Services.perms.enumerator;
     while (enumerator.hasMoreElements()) {
       let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);
 
       if (perm.type == kPermissionType &&
           perm.capability == Services.perms.DENY_ACTION) {
-        this._sites.push(perm.host);
+        this._sites.push(perm.principal.origin);
       }
     }
     Services.obs.addObserver(this, "perm-changed", false);
     this._sites.sort();
 
     this._siteTree = new Tree("sitesTree", this._sites);
     this.onSiteSelected();
 
@@ -121,24 +122,24 @@ let gTranslationExceptions = {
       else {
         let perm = aSubject.QueryInterface(Ci.nsIPermission);
         if (perm.type != kPermissionType)
           return;
 
         if (aData == "added") {
           if (perm.capability != Services.perms.DENY_ACTION)
             return;
-          this._sites.push(perm.host);
+          this._sites.push(perm.principal.origin);
           this._sites.sort();
           let boxObject = this._siteTree.boxObject;
           boxObject.rowCountChanged(0, 1);
           boxObject.invalidate();
         }
         else if (aData == "deleted") {
-          let index = this._sites.indexOf(perm.host);
+          let index = this._sites.indexOf(perm.principal.origin);
           if (index == -1)
             return;
           this._sites.splice(index, 1);
           this._siteTree.boxObject.rowCountChanged(index, -1);
           this.onSiteSelected();
           return;
         }
       }
@@ -183,32 +184,32 @@ let gTranslationExceptions = {
   },
 
   onAllLanguagesDeleted: function() {
     Services.prefs.setCharPref(kLanguagesPref, "");
   },
 
   onSiteDeleted: function() {
     let removedSites = this._siteTree.getSelectedItems();
-    for (let host of removedSites) {
-      let uri = Services.io.newURI("http://" + host, null, null);
-      Services.perms.remove(uri, kPermissionType);
+    for (let origin of removedSites) {
+      let principal = BrowserUtils.principalFromOrigin(origin);
+      Services.perms.removeFromPrincipal(principal, kPermissionType);
     }
   },
 
   onAllSitesDeleted: function() {
     if (this._siteTree.isEmpty)
       return;
 
     let removedSites = this._sites.splice(0, this._sites.length);
     this._siteTree.boxObject.rowCountChanged(0, -removedSites.length);
 
-    for (let host of removedSites) {
-      let uri = Services.io.newURI("http://" + host, null, null);
-      Services.perms.remove(uri, kPermissionType);
+    for (let origin of removedSites) {
+      let principal = BrowserUtils.principalFromOrigin(origin);
+      Services.perms.removeFromPrincipal(principal, kPermissionType);
     }
 
     this.onSiteSelected();
   },
 
   onSiteKeyPress: function(aEvent) {
     if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE)
       this.onSiteDeleted();
--- a/browser/components/sessionstore/test/browser_607016.js
+++ b/browser/components/sessionstore/test/browser_607016.js
@@ -1,57 +1,30 @@
-/* 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";
 
 let stateBackup = ss.getBrowserState();
 
-function cleanup() {
-  // Reset the pref
-  try {
-    Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
-  } catch (e) {}
-  ss.setBrowserState(stateBackup);
-  executeSoon(finish);
-}
-
-function test() {
+add_task(function* () {
   /** Bug 607016 - If a tab is never restored, attributes (eg. hidden) aren't updated correctly **/
-  waitForExplicitFinish();
   ignoreAllUncaughtExceptions();
 
   // Set the pref to true so we know exactly how many tabs should be restoring at
   // any given time. This guarantees that a finishing load won't start another.
   Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
 
-  // We have our own progress listener for this test, which we'll attach before our state is set
-  let progressListener = {
-    onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
-      if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
-          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
-          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
-          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
-        progressCallback(aBrowser);
-    }
-  }
-
   let state = { windows: [{ tabs: [
     { entries: [{ url: "http://example.org#1" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.org#2" }], extData: { "uniq": r() } }, // overwriting
     { entries: [{ url: "http://example.org#3" }], extData: { "uniq": r() } }, // hiding
     { entries: [{ url: "http://example.org#4" }], extData: { "uniq": r() } }, // adding
     { entries: [{ url: "http://example.org#5" }], extData: { "uniq": r() } }, // deleting
     { entries: [{ url: "http://example.org#6" }] } // creating
   ], selected: 1 }] };
 
-  function progressCallback(aBrowser) {
-    // We'll remove the progress listener after the first one because we aren't
-    // loading any other tabs
-    window.gBrowser.removeTabsProgressListener(progressListener);
-
+  function* progressCallback() {
     let curState = JSON.parse(ss.getBrowserState());
     for (let i = 0; i < curState.windows[0].tabs.length; i++) {
       let tabState = state.windows[0].tabs[i];
       let tabCurState = curState.windows[0].tabs[i];
       if (tabState.extData) {
         is(tabCurState.extData["uniq"], tabState.extData["uniq"],
            "sanity check that tab has correct extData");
       }
@@ -62,56 +35,65 @@ function test() {
         ok(!("extData" in tabCurState) || !("uniq" in tabCurState.extData),
            "sanity check that tab doesn't have extData or extData doesn't have 'uniq'");
       }
     }
 
     // Now we'll set a new unique value on 1 of the tabs
     let newUniq = r();
     ss.setTabValue(gBrowser.tabs[1], "uniq", newUniq);
-    gBrowser.removeTab(gBrowser.tabs[1]);
+    yield promiseRemoveTab(gBrowser.tabs[1]);
     let closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
     is(closedTabData.state.extData.uniq, newUniq,
        "(overwriting) new data is stored in extData");
 
     // hide the next tab before closing it
     gBrowser.hideTab(gBrowser.tabs[1]);
-    gBrowser.removeTab(gBrowser.tabs[1]);
+    yield promiseRemoveTab(gBrowser.tabs[1]);
     closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
     ok(closedTabData.state.hidden, "(hiding) tab data has hidden == true");
 
     // set data that's not in a conflicting key
     let stillUniq = r();
     ss.setTabValue(gBrowser.tabs[1], "stillUniq", stillUniq);
-    gBrowser.removeTab(gBrowser.tabs[1]);
+    yield promiseRemoveTab(gBrowser.tabs[1]);
     closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
     is(closedTabData.state.extData.stillUniq, stillUniq,
        "(adding) new data is stored in extData");
 
     // remove the uniq value and make sure it's not there in the closed data
     ss.deleteTabValue(gBrowser.tabs[1], "uniq");
-    gBrowser.removeTab(gBrowser.tabs[1]);
+    yield promiseRemoveTab(gBrowser.tabs[1]);
     closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
     // Since Panorama might have put data in, first check if there is extData.
     // If there is explicitly check that "uniq" isn't in it. Otherwise, we're ok
     if ("extData" in closedTabData.state) {
       ok(!("uniq" in closedTabData.state.extData),
          "(deleting) uniq not in existing extData");
     }
     else {
       ok(true, "(deleting) no data is stored in extData");
     }
 
     // set unique data on the tab that never had any set, make sure that's saved
     let newUniq2 = r();
     ss.setTabValue(gBrowser.tabs[1], "uniq", newUniq2);
-    gBrowser.removeTab(gBrowser.tabs[1]);
+    yield promiseRemoveTab(gBrowser.tabs[1]);
     closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
     is(closedTabData.state.extData.uniq, newUniq2,
        "(creating) new data is stored in extData where there was none");
-
-    cleanup();
   }
 
-  window.gBrowser.addTabsProgressListener(progressListener);
+  // Set the test state.
   ss.setBrowserState(JSON.stringify(state));
-}
+
+  // Wait until the selected tab is restored and all others are pending.
+  yield Promise.all(Array.map(gBrowser.tabs, tab => {
+    return (tab == gBrowser.selectedTab) ?
+      promiseTabRestored(tab) : promiseTabRestoring(tab)
+  }));
 
+  // Kick off the actual tests.
+  yield progressCallback();
+
+  // Cleanup.
+  yield promiseBrowserState(stateBackup);
+});
--- a/browser/components/translation/test/browser_translation_exceptions.js
+++ b/browser/components/translation/test/browser_translation_exceptions.js
@@ -46,17 +46,17 @@ function getLanguageExceptions() {
 function getDomainExceptions() {
   let results = [];
   let enumerator = Services.perms.enumerator;
   while (enumerator.hasMoreElements()) {
     let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);
 
     if (perm.type == "translate" &&
         perm.capability == Services.perms.DENY_ACTION)
-      results.push(perm.host);
+      results.push(perm.principal);
   }
 
   return results;
 }
 
 function getInfoBar() {
   return gBrowser.getNotificationBox().getNotificationWithValue("translation");
 }
@@ -176,17 +176,17 @@ let gTests = [
 
     // Click the 'Never for French' item.
     notif._getAnonElt("neverForSite").click();
     ok(!getInfoBar(), "infobar hidden");
 
     // Check this has been saved to the exceptions list.
     let sites = getDomainExceptions();
     is(sites.length, 1, "one site in the exception list");
-    is(sites[0], "example.com", "correct site in the exception list");
+    is(sites[0].origin, "http://example.com", "correct site in the exception list");
     ok(!ui.shouldShowInfoBar(uri, "fr"),
        "the infobar wouldn't be shown anymore");
 
     // Reopen the infobar.
     PopupNotifications.getNotification("translate").anchorElement.click();
     notif = getInfoBar();
     // Open the "options" drop down.
     yield openPopup(notif._getAnonElt("options"));
--- a/browser/components/uitour/test/head.js
+++ b/browser/components/uitour/test/head.js
@@ -193,29 +193,32 @@ function loadUITourTestPage(callback, ho
     gContentAPI = gContentWindow.Mozilla.UITour;
 
     waitForFocus(callback, gContentWindow);
   }, true);
 }
 
 function UITourTest() {
   Services.prefs.setBoolPref("browser.uitour.enabled", true);
-  let testUri = Services.io.newURI("http://example.com", null, null);
-  Services.perms.add(testUri, "uitour", Services.perms.ALLOW_ACTION);
+  let testHttpsUri = Services.io.newURI("https://example.com", null, null);
+  let testHttpUri = Services.io.newURI("http://example.com", null, null);
+  Services.perms.add(testHttpsUri, "uitour", Services.perms.ALLOW_ACTION);
+  Services.perms.add(testHttpUri, "uitour", Services.perms.ALLOW_ACTION);
 
   waitForExplicitFinish();
 
   registerCleanupFunction(function() {
     delete window.gContentWindow;
     delete window.gContentAPI;
     if (gTestTab)
       gBrowser.removeTab(gTestTab);
     delete window.gTestTab;
     Services.prefs.clearUserPref("browser.uitour.enabled", true);
-    Services.perms.remove(testUri, "uitour");
+    Services.perms.remove(testHttpsUri, "uitour");
+    Services.perms.remove(testHttpUri, "uitour");
   });
 
   function done() {
     executeSoon(() => {
       if (gTestTab)
         gBrowser.removeTab(gTestTab);
       gTestTab = null;
 
--- a/browser/devtools/.eslintrc
+++ b/browser/devtools/.eslintrc
@@ -14,16 +14,17 @@
     "EventEmitter": true,
     "exports": true,
     "loader": true,
     "module": true,
     "require": true,
     "Services": true,
     "Task": true,
     "XPCOMUtils": true,
+    "XPCNativeWrapper": true,
   },
   "rules": {
     // These are the rules that have been configured so far to match the
     // devtools coding style.
 
     // Disallow using variables outside the blocks they are defined (especially
     // since only let and const are used, see "no-var").
     "block-scoped-var": 2,
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-editor.js
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-editor.js
@@ -219,17 +219,17 @@ function test() {
       "The second source is no longer displayed.");
     is(gEditor.getText().indexOf("firstCall"), 118,
       "The first source is displayed.");
 
     is(gSources.values[0], gSources.selectedValue,
       "The first source should be currently selected.");
 
     let window = gEditor.container.contentWindow;
-    executeSoon(() => window.mozRequestAnimationFrame(onReadyForClick));
+    executeSoon(() => window.requestAnimationFrame(onReadyForClick));
   }
 
   function onReadyForClick() {
     info("Remove the second breakpoint using the mouse.");
     gEditor.once("breakpointRemoved", onEditorBreakpointRemoveSecond);
 
     let iframe = gEditor.container;
     let testWin = iframe.ownerDocument.defaultView;
--- a/browser/devtools/inspector/inspector-panel.js
+++ b/browser/devtools/inspector/inspector-panel.js
@@ -1124,17 +1124,20 @@ InspectorPanel.prototype = {
 
   /**
    * Initiate gcli screenshot command on selected node
    */
   screenshotNode: function() {
     CommandUtils.createRequisition(this._target, {
       environment: CommandUtils.createEnvironment(this, '_target')
     }).then(requisition => {
-      requisition.updateExec("screenshot --selector " + this.selectionCssSelector);
+      // Bug 1180314 -  CssSelector might contain white space so need to make sure it is
+      // passed to screenshot as a single parameter.  More work *might* be needed if
+      // CssSelector could contain escaped single- or double-quotes, backslashes, etc.
+      requisition.updateExec("screenshot --selector '" + this.selectionCssSelector + "'");
     });
   },
 
   /**
    * Scroll the node into view.
    */
   scrollNodeIntoView: function() {
     if (!this.selection.isNode()) {
--- a/browser/devtools/responsivedesign/responsivedesign.jsm
+++ b/browser/devtools/responsivedesign/responsivedesign.jsm
@@ -13,18 +13,19 @@ Cu.import("resource:///modules/devtools/
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
                                   "resource://gre/modules/SystemAppProxy.jsm");
 
 var require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
-let {showDoorhanger} = require("devtools/shared/doorhanger");
-let {TouchEventHandler} = require("devtools/touch-events");
+let { showDoorhanger } = require("devtools/shared/doorhanger");
+let { TouchEventSimulator } = require("devtools/toolkit/touch/simulator");
+let { Task } = require("resource://gre/modules/Task.jsm");
 
 this.EXPORTED_SYMBOLS = ["ResponsiveUIManager"];
 
 const MIN_WIDTH = 50;
 const MIN_HEIGHT = 50;
 
 const MAX_WIDTH = 10000;
 const MAX_HEIGHT = 10000;
@@ -129,17 +130,16 @@ function ResponsiveUI(aWindow, aTab)
   this.tab = aTab;
   this.mm = this.tab.linkedBrowser.messageManager;
   this.tabContainer = aWindow.gBrowser.tabContainer;
   this.browser = aTab.linkedBrowser;
   this.chromeDoc = aWindow.document;
   this.container = aWindow.gBrowser.getBrowserContainer(this.browser);
   this.stack = this.container.querySelector(".browserStack");
   this._telemetry = new Telemetry();
-  this.e10s = !this.browser.contentWindow;
 
   let childOn = () => {
     this.mm.removeMessageListener("ResponsiveMode:Start:Done", childOn);
     ResponsiveUIManager.emit("on", { tab: this.tab });
   }
   this.mm.addMessageListener("ResponsiveMode:Start:Done", childOn);
 
   let requiresFloatingScrollbars = !this.mainWindow.matchMedia("(-moz-overlay-scrollbars)").matches;
@@ -214,21 +214,19 @@ function ResponsiveUI(aWindow, aTab)
       this.rotate();
     }
   } catch(e) {}
 
   ActiveTabs.set(aTab, this);
 
   this._telemetry.toolOpened("responsive");
 
-  if (!this.e10s) {
-    // Touch events support
-    this.touchEnableBefore = false;
-    this.touchEventHandler = new TouchEventHandler(this.browser);
-  }
+  // Touch events support
+  this.touchEnableBefore = false;
+  this.touchEventSimulator = new TouchEventSimulator(this.browser);
 
   // Hook to display promotional Developer Edition doorhanger. Only displayed once.
   showDoorhanger({
     window: this.mainWindow,
     type: "deveditionpromo",
     anchor: this.chromeDoc.querySelector("#content")
   });
 }
@@ -269,19 +267,17 @@ ResponsiveUI.prototype = {
     this.menulist.removeEventListener("change", this.bound_handleManualInput, true);
     this.tab.removeEventListener("TabClose", this);
     this.tabContainer.removeEventListener("TabSelect", this);
     this.rotatebutton.removeEventListener("command", this.bound_rotate, true);
     this.screenshotbutton.removeEventListener("command", this.bound_screenshot, true);
     this.closebutton.removeEventListener("command", this.bound_close, true);
     this.addbutton.removeEventListener("command", this.bound_addPreset, true);
     this.removebutton.removeEventListener("command", this.bound_removePreset, true);
-    if (!this.e10s) {
-      this.touchbutton.removeEventListener("command", this.bound_touch, true);
-    }
+    this.touchbutton.removeEventListener("command", this.bound_touch, true);
 
     // Removed elements.
     this.container.removeChild(this.toolbar);
     if (this.bottomToolbar) {
       this.bottomToolbar.remove();
       delete this.bottomToolbar;
     }
     this.stack.removeChild(this.resizer);
@@ -290,18 +286,18 @@ ResponsiveUI.prototype = {
 
     this.stack.classList.remove("fxos-mode");
 
     // Unset the responsive mode.
     this.container.removeAttribute("responsivemode");
     this.stack.removeAttribute("responsivemode");
 
     ActiveTabs.delete(this.tab);
-    if (!this.e10s && this.touchEventHandler) {
-      this.touchEventHandler.stop();
+    if (this.touchEventSimulator) {
+      this.touchEventSimulator.stop();
     }
     this._telemetry.toolClosed("responsive");
     let childOff = () => {
       this.mm.removeMessageListener("ResponsiveMode:Stop:Done", childOff);
       ResponsiveUIManager.emit("off", { tab: this.tab });
     }
     this.mm.addMessageListener("ResponsiveMode:Stop:Done", childOff);
     this.tab.linkedBrowser.messageManager.sendAsyncMessage("ResponsiveMode:Stop");
@@ -436,24 +432,22 @@ ResponsiveUI.prototype = {
     this.closebutton.className = "devtools-responsiveui-toolbarbutton devtools-responsiveui-close";
     this.closebutton.setAttribute("tooltiptext", this.strings.GetStringFromName("responsiveUI.close"));
     this.closebutton.addEventListener("command", this.bound_close, true);
 
     this.toolbar.appendChild(this.closebutton);
     this.toolbar.appendChild(this.menulist);
     this.toolbar.appendChild(this.rotatebutton);
 
-    if (!this.e10s) {
-      this.touchbutton = this.chromeDoc.createElement("toolbarbutton");
-      this.touchbutton.setAttribute("tabindex", "0");
-      this.touchbutton.setAttribute("tooltiptext", this.strings.GetStringFromName("responsiveUI.touch"));
-      this.touchbutton.className = "devtools-responsiveui-toolbarbutton devtools-responsiveui-touch";
-      this.touchbutton.addEventListener("command", this.bound_touch, true);
-      this.toolbar.appendChild(this.touchbutton);
-    }
+    this.touchbutton = this.chromeDoc.createElement("toolbarbutton");
+    this.touchbutton.setAttribute("tabindex", "0");
+    this.touchbutton.setAttribute("tooltiptext", this.strings.GetStringFromName("responsiveUI.touch"));
+    this.touchbutton.className = "devtools-responsiveui-toolbarbutton devtools-responsiveui-touch";
+    this.touchbutton.addEventListener("command", this.bound_touch, true);
+    this.toolbar.appendChild(this.touchbutton);
 
     this.toolbar.appendChild(this.screenshotbutton);
 
     // Resizers
     let resizerTooltip = this.strings.GetStringFromName("responsiveUI.resizerTooltip");
     this.resizer = this.chromeDoc.createElement("box");
     this.resizer.className = "devtools-responsiveui-resizehandle";
     this.resizer.setAttribute("right", "0");
@@ -788,45 +782,39 @@ ResponsiveUI.prototype = {
     mm.addMessageListener("ResponsiveMode:RequestScreenshot:Done", onScreenshot);
     mm.sendAsyncMessage("ResponsiveMode:RequestScreenshot");
   },
 
   /**
    * Enable/Disable mouse -> touch events translation.
    */
    enableTouch: function RUI_enableTouch() {
-     if (!this.touchEventHandler.enabled) {
-       let isReloadNeeded = this.touchEventHandler.start();
-       this.touchbutton.setAttribute("checked", "true");
-       return isReloadNeeded;
-     }
-     return false;
+     this.touchbutton.setAttribute("checked", "true");
+     return this.touchEventSimulator.start();
    },
 
    disableTouch: function RUI_disableTouch() {
-     if (this.touchEventHandler.enabled) {
-       this.touchEventHandler.stop();
-       this.touchbutton.removeAttribute("checked");
-     }
+     this.touchbutton.removeAttribute("checked");
+     return this.touchEventSimulator.stop();
    },
 
    hideTouchNotification: function RUI_hideTouchNotification() {
      let nbox = this.mainWindow.gBrowser.getNotificationBox(this.browser);
      let n = nbox.getNotificationWithValue("responsive-ui-need-reload");
      if (n) {
        n.close();
      }
    },
 
-   toggleTouch: function RUI_toggleTouch() {
+   toggleTouch: Task.async(function*() {
      this.hideTouchNotification();
-     if (this.touchEventHandler.enabled) {
+     if (this.touchEventSimulator.enabled) {
        this.disableTouch();
      } else {
-       let isReloadNeeded = this.enableTouch();
+       let isReloadNeeded = yield this.enableTouch();
        if (isReloadNeeded) {
          if (Services.prefs.getBoolPref("devtools.responsiveUI.no-reload-notification")) {
            return;
          }
 
          let nbox = this.mainWindow.gBrowser.getNotificationBox(this.browser);
 
          var buttons = [{
@@ -846,17 +834,17 @@ ResponsiveUI.prototype = {
          nbox.appendNotification(
            this.strings.GetStringFromName("responsiveUI.needReload"),
            "responsive-ui-need-reload",
            null,
            nbox.PRIORITY_INFO_LOW,
            buttons);
        }
      }
-   },
+   }),
 
   /**
    * Change the size of the browser.
    *
    * @param aWidth width of the browser.
    * @param aHeight height of the browser.
    */
   setSize: function RUI_setSize(aWidth, aHeight) {
--- a/browser/devtools/responsivedesign/test/browser.ini
+++ b/browser/devtools/responsivedesign/test/browser.ini
@@ -7,11 +7,10 @@ support-files =
 
 [browser_responsive_cmd.js]
 [browser_responsivecomputedview.js]
 skip-if = e10s # Bug ??????
 [browser_responsiveruleview.js]
 skip-if = e10s # Bug ??????
 [browser_responsiveui.js]
 [browser_responsiveui_touch.js]
-skip-if = e10s # Bug ?????? - [e10s] re-introduce touch feature in responsive mode
 [browser_responsiveuiaddcustompreset.js]
 [browser_responsive_devicewidth.js]
--- a/browser/devtools/responsivedesign/test/browser_responsiveui_touch.js
+++ b/browser/devtools/responsivedesign/test/browser_responsiveui_touch.js
@@ -1,66 +1,50 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-function test() {
-  let url = "http://mochi.test:8888/browser/browser/devtools/responsivedesign/test/touch.html";
-
-  let mgr = ResponsiveUI.ResponsiveUIManager;
-
-  waitForExplicitFinish();
+"use strict";
 
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onload() {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    waitForFocus(startTest, content);
-  }, true);
-
-  content.location = url;
+const TEST_URI = "http://mochi.test:8888/browser/browser/devtools/" +
+                 "responsivedesign/test/touch.html";
 
-  function startTest() {
-    mgr.once("on", function() {executeSoon(testWithNoTouch)});
-    mgr.once("off", function() {executeSoon(finishUp)});
-    mgr.toggle(window, gBrowser.selectedTab);
-  }
-
-  function testWithNoTouch() {
-    let div = content.document.querySelector("div");
-    let x = 2, y = 2;
-    EventUtils.synthesizeMouse(div, x, y, {type: "mousedown", isSynthesized: false}, content);
-    x += 20; y += 10;
-    EventUtils.synthesizeMouse(div, x, y, {type: "mousemove", isSynthesized: false}, content);
-    is(div.style.transform, "", "touch didn't work");
-    EventUtils.synthesizeMouse(div, x, y, {type: "mouseup", isSynthesized: false}, content);
-    testWithTouch();
-  }
+add_task(function*() {
+  yield addTab(TEST_URI);
+  let mgr = ResponsiveUI.ResponsiveUIManager;
+  let mgrOn = once(mgr, "on");
+  mgr.toggle(window, gBrowser.selectedTab);
+  yield mgrOn;
+  yield testWithNoTouch();
+  yield mgr.getResponsiveUIForTab(gBrowser.selectedTab).enableTouch();
+  yield testWithTouch();
+  yield mgr.getResponsiveUIForTab(gBrowser.selectedTab).disableTouch();
+  yield testWithNoTouch();
+  let mgrOff = once(mgr, "off");
+  mgr.toggle(window, gBrowser.selectedTab);
+  yield mgrOff;
+});
 
-  function testWithTouch() {
-    mgr.getResponsiveUIForTab(gBrowser.selectedTab).enableTouch();
-    let div = content.document.querySelector("div");
-    let x = 2, y = 2;
-    EventUtils.synthesizeMouse(div, x, y, {type: "mousedown", isSynthesized: false}, content);
-    x += 20; y += 10;
-    EventUtils.synthesizeMouse(div, x, y, {type: "mousemove", isSynthesized: false}, content);
-    is(div.style.transform, "translate(20px, 10px)", "touch worked");
-    EventUtils.synthesizeMouse(div, x, y, {type: "mouseup", isSynthesized: false}, content);
-    is(div.style.transform, "none", "end event worked");
-    mgr.toggle(window, gBrowser.selectedTab);
-  }
+function* testWithNoTouch() {
+  let div = content.document.querySelector("div");
+  let x = 2, y = 2;
+  yield BrowserTestUtils.synthesizeMouse("div", x, y,
+        { type: "mousedown", isSynthesized: false }, gBrowser.selectedBrowser);
+  x += 20; y += 10;
+  yield BrowserTestUtils.synthesizeMouse("div", x, y,
+        { type: "mousemove", isSynthesized: false }, gBrowser.selectedBrowser);
+  is(div.style.transform, "none", "touch didn't work");
+  yield BrowserTestUtils.synthesizeMouse("div", x, y,
+        { type: "mouseup", isSynthesized: false }, gBrowser.selectedBrowser);
+}
 
-  function testWithTouchAgain() {
-    mgr.getResponsiveUIForTab(gBrowser.selectedTab).disableTouch();
-    let div = content.document.querySelector("div");
-    let x = 2, y = 2;
-    EventUtils.synthesizeMouse(div, x, y, {type: "mousedown", isSynthesized: false}, content);
-    x += 20; y += 10;
-    EventUtils.synthesizeMouse(div, x, y, {type: "mousemove", isSynthesized: false}, content);
-    is(div.style.transform, "", "touch didn't work");
-    EventUtils.synthesizeMouse(div, x, y, {type: "mouseup", isSynthesized: false}, content);
-    finishUp();
-  }
-
-
-  function finishUp() {
-    gBrowser.removeCurrentTab();
-    finish();
-  }
+function* testWithTouch() {
+  let div = content.document.querySelector("div");
+  let x = 2, y = 2;
+  yield BrowserTestUtils.synthesizeMouse("div", x, y,
+        { type: "mousedown", isSynthesized: false }, gBrowser.selectedBrowser);
+  x += 20; y += 10;
+  yield BrowserTestUtils.synthesizeMouse("div", x, y,
+        { type: "mousemove", isSynthesized: false }, gBrowser.selectedBrowser);
+  is(div.style.transform, "translate(20px, 10px)", "touch worked");
+  yield BrowserTestUtils.synthesizeMouse("div", x, y,
+        { type: "mouseup", isSynthesized: false }, gBrowser.selectedBrowser);
+  is(div.style.transform, "none", "end event worked");
 }
--- a/browser/devtools/responsivedesign/test/head.js
+++ b/browser/devtools/responsivedesign/test/head.js
@@ -9,18 +9,21 @@ let {devtools} = Cu.import("resource:///
 let TargetFactory = devtools.TargetFactory;
 let DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
 
 // Import the GCLI test helper
 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
 
 DevToolsUtils.testing = true;
-SimpleTest.registerCleanupFunction(() => {
+registerCleanupFunction(() => {
   DevToolsUtils.testing = false;
+  while (gBrowser.tabs.length > 1) {
+    gBrowser.removeCurrentTab();
+  }
 });
 
 /**
  * Open the toolbox, with the inspector tool visible.
  * @return a promise that resolves when the inspector is ready
  */
 let openInspector = Task.async(function*() {
   info("Opening the inspector");
--- a/browser/devtools/responsivedesign/test/touch.html
+++ b/browser/devtools/responsivedesign/test/touch.html
@@ -12,16 +12,17 @@
 </style>
 
 <div></div>
 
 <script>
   var div = document.querySelector("div");
   var initX, initY;
 
+  div.style.transform = "none";
 
   div.addEventListener("touchstart", function(evt) {
     var touch = evt.changedTouches[0];
     initX = touch.pageX;
     initY = touch.pageY;
   }, true);
 
   div.addEventListener("touchmove", function(evt) {
--- a/browser/devtools/sourceeditor/editor.js
+++ b/browser/devtools/sourceeditor/editor.js
@@ -31,16 +31,18 @@ const RE_JUMP_TO_LINE = /^(\d+):?(\d+)?/
 
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const events  = require("devtools/toolkit/event-emitter");
 const { PrefObserver } = require("devtools/styleeditor/utils");
 
 Cu.import("resource://gre/modules/Services.jsm");
 const L10N = Services.strings.createBundle(L10N_BUNDLE);
 
+const { OS } = Services.appinfo;
+
 // CM_STYLES, CM_SCRIPTS and CM_IFRAME represent the HTML,
 // JavaScript and CSS that is injected into an iframe in
 // order to initialize a CodeMirror instance.
 
 const CM_STYLES   = [
   "chrome://browser/skin/devtools/common.css",
   "chrome://browser/content/devtools/codemirror/codemirror.css",
   "chrome://browser/content/devtools/codemirror/dialog.css",
@@ -294,16 +296,72 @@ Editor.prototype = {
         ev.preventDefault();
         if (!this.config.contextMenu) return;
         let popup = this.config.contextMenu;
         if (typeof popup == "string")
           popup = el.ownerDocument.getElementById(this.config.contextMenu);
         popup.openPopupAtScreen(ev.screenX, ev.screenY, true);
       }, false);
 
+      // Intercept the find and find again keystroke on CodeMirror, to avoid
+      // the browser's search
+
+      let findKey = L10N.GetStringFromName("find.commandkey");
+      let findAgainKey = L10N.GetStringFromName("findAgain.commandkey");
+      let [accel, modifier] = OS === "Darwin"
+                                      ? ["metaKey", "altKey"]
+                                      : ["ctrlKey", "shiftKey"];
+
+      cm.getWrapperElement().addEventListener("keydown", (ev) => {
+        let key = ev.key.toUpperCase();
+        let node = ev.originalTarget;
+        let isInput = node.tagName === "INPUT";
+        let isSearchInput = isInput && node.type === "search";
+
+        // replace box is a different input instance than search, and it is
+        // located in a code mirror dialog
+        let isDialogInput = isInput &&
+                       node.parentNode &&
+                       node.parentNode.classList.contains("CodeMirror-dialog");
+
+        if (!ev[accel] || !(isSearchInput || isDialogInput)) return;
+
+        if (key === findKey) {
+          ev.preventDefault();
+
+          if (isSearchInput || ev[modifier]) {
+            node.select();
+          }
+        } else if (key === findAgainKey) {
+          ev.preventDefault();
+
+          if (!isSearchInput) return;
+
+          let query = node.value;
+
+          // If there isn't a search state, or the text in the input does not
+          // match with the current search state, we need to create a new one
+          if (!cm.state.search || cm.state.search.query !== query) {
+            cm.state.search = {
+              posFrom: null,
+              posTo: null,
+              overlay: null,
+              query
+            };
+          }
+
+          if (ev.shiftKey) {
+            cm.execCommand("findPrev");
+          } else {
+            cm.execCommand("findNext");
+          }
+        }
+      });
+
+
       cm.on("focus", () => this.emit("focus"));
       cm.on("scroll", () => this.emit("scroll"));
       cm.on("change", () => {
         this.emit("change");
         if (!this._lastDirty) {
           this._lastDirty = true;
           this.emit("dirty-change");
         }
--- a/browser/devtools/sourceeditor/test/browser.ini
+++ b/browser/devtools/sourceeditor/test/browser.ini
@@ -23,16 +23,17 @@ support-files =
   head.js
   helper_codemirror_runner.js
 
 [browser_editor_autocomplete_basic.js]
 [browser_editor_autocomplete_events.js]
 [browser_editor_autocomplete_js.js]
 [browser_editor_basic.js]
 [browser_editor_cursor.js]
+[browser_editor_find_again.js]
 [browser_editor_goto_line.js]
 [browser_editor_history.js]
 [browser_editor_markers.js]
 [browser_editor_movelines.js]
 [browser_editor_prefs.js]
 [browser_editor_script_injection.js]
 [browser_editor_addons.js]
 [browser_codemirror.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/test/browser_editor_find_again.js
@@ -0,0 +1,214 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const L10N_BUNDLE = "chrome://browser/locale/devtools/sourceeditor.properties";
+const L10N = Services.strings.createBundle(L10N_BUNDLE);
+
+const FIND_KEY = L10N.GetStringFromName("find.commandkey");
+const FINDAGAIN_KEY = L10N.GetStringFromName("findAgain.commandkey");
+
+const { OS } = Services.appinfo;
+
+// On linux, getting immediately the selection's range here fails, returning
+// values like it's not selected – even if the selection is visible.
+// For the record, setting the selection's range immediately doesn't have
+// any effect.
+// It's like the <input> is not ready yet.
+// Therefore, we trigger the UI focus event to the <input>, waiting for the
+// response.
+// Using a timeout could also work, but that is more precise, ensuring also
+// the execution of the listeners added to the <input>'s focus.
+const dispatchAndWaitForFocus = (target) => new Promise((resolve) => {
+  target.addEventListener("focus", function listener() {
+    target.removeEventListener("focus", listener);
+    resolve(target);
+  });
+
+  target.dispatchEvent(new UIEvent("focus"));
+});
+
+function openSearchBox(ed) {
+  let edDoc = ed.container.contentDocument;
+  let edWin = edDoc.defaultView;
+
+  let input = edDoc.querySelector("input[type=search]");
+  ok(!input, "search box closed");
+
+  // The editor needs the focus to properly receive the `synthesizeKey`
+  ed.focus();
+
+  EventUtils.synthesizeKey(FINDAGAIN_KEY, { accelKey: true }, edWin);
+
+  input = edDoc.querySelector("input[type=search]");
+  ok(input, "find again command key opens the search box");
+}
+
+function testFindAgain (ed, inputLine, expectCursor, shiftKey=false) {
+  let edDoc = ed.container.contentDocument;
+  let edWin = edDoc.defaultView;
+
+  let input = edDoc.querySelector("input[type=search]");
+  input.value = inputLine;
+
+  // Ensure the input has the focus before send the key – necessary on Linux,
+  // it seems that during the tests can be lost
+  input.focus();
+
+  EventUtils.synthesizeKey(FINDAGAIN_KEY, { accelKey: true, shiftKey }, edWin);
+
+  ch(ed.getCursor(), expectCursor,
+    "find: " + inputLine + " expects cursor: " + expectCursor.toSource());
+}
+
+const testSearchBoxTextIsSelected = Task.async(function*(ed) {
+  let edDoc = ed.container.contentDocument;
+  let edWin = edDoc.defaultView;
+
+  let input = edDoc.querySelector("input[type=search]");
+  ok(input, "search box is opened");
+
+  // Ensure the input has the focus before send the key – necessary on Linux,
+  // it seems that during the tests can be lost
+  input.focus();
+
+  // Close search box
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, edWin);
+
+  input = edDoc.querySelector("input[type=search]");
+  ok(!input, "search box is closed");
+
+  // Re-open the search box
+  EventUtils.synthesizeKey(FIND_KEY, { accelKey: true }, edWin);
+
+  input = edDoc.querySelector("input[type=search]");
+  ok(input, "find command key opens the search box");
+
+  yield dispatchAndWaitForFocus(input);
+
+  let { selectionStart, selectionEnd, value } = input;
+
+  ok(selectionStart === 0 && selectionEnd === value.length,
+    "search box's text is selected when re-opened");
+
+  // Removing selection
+  input.setSelectionRange(0, 0);
+
+  EventUtils.synthesizeKey(FIND_KEY, { accelKey: true }, edWin);
+
+  ({ selectionStart, selectionEnd } = input);
+
+  ok(selectionStart === 0 && selectionEnd === value.length,
+    "search box's text is selected when find key is pressed");
+
+  // Close search box
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, edWin);
+});
+
+const testReplaceBoxTextIsSelected = Task.async(function*(ed) {
+  let edDoc = ed.container.contentDocument;
+  let edWin = edDoc.defaultView;
+
+  let input = edDoc.querySelector(".CodeMirror-dialog > input");
+  ok(!input, "dialog box with replace is closed");
+
+  // The editor needs the focus to properly receive the `synthesizeKey`
+  ed.focus();
+
+  // Send the replace's key with the appropriate modifiers based on OS
+  let [altKey, shiftKey] = OS === "Darwin" ? [true, false] : [false, true];
+
+  EventUtils.synthesizeKey(FIND_KEY,
+    { accelKey: true, altKey, shiftKey }, edWin);
+
+  input = edDoc.querySelector(".CodeMirror-dialog > input");
+  ok(input, "dialog box with replace is opened");
+
+  input.value = "line 5";
+
+  // Ensure the input has the focus before send the key – necessary on Linux,
+  // it seems that during the tests can be lost
+  input.focus();
+
+  yield dispatchAndWaitForFocus(input);
+
+  let { selectionStart, selectionEnd, value } = input;
+
+  ok(!(selectionStart === 0 && selectionEnd === value.length),
+    "Text in dialog box is not selected");
+
+  EventUtils.synthesizeKey(FIND_KEY,
+    { accelKey: true, altKey, shiftKey }, edWin);
+
+  ({ selectionStart, selectionEnd } = input);
+
+  ok(selectionStart === 0 && selectionEnd === value.length,
+    "dialog box's text is selected when replace key is pressed");
+
+  // Close dialog box
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, edWin);
+});
+
+add_task(function*() {
+  let { ed, win } = yield setup();
+
+  ed.setText([
+    "// line 1",
+    "//  line 2",
+    "//   line 3",
+    "//    line 4",
+    "//     line 5"
+  ].join("\n"));
+
+  yield promiseWaitForFocus();
+
+  openSearchBox(ed);
+
+  let testVectors = [
+    // Starting here expect data needs to get updated for length changes to
+    // "textLines" above.
+    ["line",
+     {line: 0, ch: 7}],
+    ["line",
+     {line: 1, ch: 8}],
+    ["line",
+     {line: 2, ch: 9}],
+    ["line",
+     {line: 3, ch: 10}],
+    ["line",
+     {line: 4, ch: 11}],
+    ["ne 3",
+     {line: 2, ch: 11}],
+    ["line 1",
+      {line: 0, ch: 9}],
+    // Testing find prev
+    ["line",
+      {line: 4, ch: 11},
+      true],
+    ["line",
+      {line: 3, ch: 10},
+      true],
+    ["line",
+      {line: 2, ch: 9},
+      true],
+    ["line",
+      {line: 1, ch: 8},
+      true],
+    ["line",
+      {line: 0, ch: 7},
+      true]
+  ];
+
+  for (let v of testVectors) {
+    yield testFindAgain(ed, ...v);
+  }
+
+  yield testSearchBoxTextIsSelected(ed);
+
+  yield testReplaceBoxTextIsSelected(ed);
+
+  teardown(ed, win);
+});
--- a/browser/devtools/sourceeditor/test/head.js
+++ b/browser/devtools/sourceeditor/test/head.js
@@ -35,16 +35,21 @@ function addTab(aURL, aCallback) {
   browser.addEventListener("load", onTabLoad, true);
 }
 
 function promiseTab(aURL) {
   return new Promise(resolve =>
     addTab(aURL, resolve));
 }
 
+function promiseWaitForFocus() {
+  return new Promise(resolve =>
+    waitForFocus(resolve));
+}
+
 function setup(cb, additionalOpts = {}) {
   cb = cb || function() {};
   let def = promise.defer();
   const opt = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
   const url = "data:application/vnd.mozilla.xul+xml;charset=UTF-8,<?xml version='1.0'?>" +
     "<?xml-stylesheet href='chrome://global/skin/global.css'?>" +
     "<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'" +
     " title='Editor' width='600' height='500'><box flex='1'/></window>";
--- a/browser/devtools/styleinspector/computed-view.js
+++ b/browser/devtools/styleinspector/computed-view.js
@@ -1,35 +1,39 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
+/* globals overlays, StyleInspectorMenu */
+
+"use strict";
+
 const {Cc, Ci, Cu} = require("chrome");
 
 const ToolDefinitions = require("main").Tools;
 const {CssLogic} = require("devtools/styleinspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/server/actors/styles");
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
-const {EventEmitter} = require("devtools/toolkit/event-emitter");
 const {OutputParser} = require("devtools/output-parser");
 const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/styleeditor/utils");
 const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-const overlays = require("devtools/styleinspector/style-inspector-overlays");
+
+loader.lazyRequireGetter(this, "overlays", "devtools/styleinspector/style-inspector-overlays");
+loader.lazyRequireGetter(this, "StyleInspectorMenu", "devtools/styleinspector/style-inspector-menu");
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
 
 const FILTER_CHANGED_TIMEOUT = 150;
 const HTML_NS = "http://www.w3.org/1999/xhtml";
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 /**
  * Helper for long-running processes that should yield occasionally to
  * the mainloop.
  *
  * @param {Window} aWin
  *        Timeouts will be set on this window when appropriate.
  * @param {Generator} aGenerator
@@ -45,17 +49,17 @@ const XUL_NS = "http://www.mozilla.org/k
  *
  * @constructor
  */
 function UpdateProcess(aWin, aGenerator, aOptions)
 {
   this.win = aWin;
   this.iter = _Iterator(aGenerator);
   this.onItem = aOptions.onItem || function() {};
-  this.onBatch = aOptions.onBatch || function () {};
+  this.onBatch = aOptions.onBatch || function() {};
   this.onDone = aOptions.onDone || function() {};
   this.onCancel = aOptions.onCancel || function() {};
   this.threshold = aOptions.threshold || 45;
 
   this.canceled = false;
 }
 
 UpdateProcess.prototype = {
@@ -111,53 +115,48 @@ UpdateProcess.prototype = {
         this.onBatch();
         return;
       }
     }
   }
 };
 
 /**
- * CssHtmlTree is a panel that manages the display of a table sorted by style.
- * There should be one instance of CssHtmlTree per style display (of which there
+ * CssComputedView is a panel that manages the display of a table sorted by style.
+ * There should be one instance of CssComputedView per style display (of which there
  * will generally only be one).
  *
- * @params {StyleInspector} aStyleInspector The owner of this CssHtmlTree
- * @param {PageStyleFront} aPageStyle
+ * @param {Inspector} inspector toolbox panel
+ * @param {Document} document The document that will contain the computed view.
+ * @param {PageStyleFront} pageStyle
  *        Front for the page style actor that will be providing
  *        the style information.
  *
  * @constructor
  */
-function CssHtmlTree(aStyleInspector, aPageStyle)
-{
-  this.styleWindow = aStyleInspector.doc.defaultView;
-  this.styleDocument = aStyleInspector.doc;
-  this.styleInspector = aStyleInspector;
-  this.inspector = this.styleInspector.inspector;
-  this.pageStyle = aPageStyle;
+function CssComputedView(inspector, document, pageStyle) {
+  this.inspector = inspector;
+  this.styleDocument = document;
+  this.styleWindow = this.styleDocument.defaultView;
+  this.pageStyle = pageStyle;
+
   this.propertyViews = [];
 
   this._outputParser = new OutputParser();
 
   let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
     getService(Ci.nsIXULChromeRegistry);
   this.getRTLAttr = chromeReg.isLocaleRTL("global") ? "rtl" : "ltr";
 
   // Create bound methods.
   this.focusWindow = this.focusWindow.bind(this);
   this._onKeypress = this._onKeypress.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
-  this._contextMenuUpdate = this._contextMenuUpdate.bind(this);
-  this._onSelectAll = this._onSelectAll.bind(this);
   this._onClick = this._onClick.bind(this);
   this._onCopy = this._onCopy.bind(this);
-  this._onCopyColor = this._onCopyColor.bind(this);
-  this._onCopyUrl = this._onCopyUrl.bind(this);
-  this._onCopyImageDataUrl = this._onCopyImageDataUrl.bind(this);
   this._onFilterStyles = this._onFilterStyles.bind(this);
   this._onFilterKeyPress = this._onFilterKeyPress.bind(this);
   this._onClearSearch = this._onClearSearch.bind(this);
   this._onIncludeBrowserStyles = this._onIncludeBrowserStyles.bind(this);
   this._onFilterTextboxContextMenu = this._onFilterTextboxContextMenu.bind(this);
 
   let doc = this.styleDocument;
   this.root = doc.getElementById("root");
@@ -183,59 +182,61 @@ function CssHtmlTree(aStyleInspector, aP
   // No results text.
   this.noResults = this.styleDocument.getElementById("noResults");
 
   // Refresh panel when color unit changed.
   this._handlePrefChange = this._handlePrefChange.bind(this);
   gDevTools.on("pref-changed", this._handlePrefChange);
 
   // Refresh panel when pref for showing original sources changes
-  this._updateSourceLinks = this._updateSourceLinks.bind(this);
+  this._onSourcePrefChanged = this._onSourcePrefChanged.bind(this);
   this._prefObserver = new PrefObserver("devtools.");
-  this._prefObserver.on(PREF_ORIG_SOURCES, this._updateSourceLinks);
+  this._prefObserver.on(PREF_ORIG_SOURCES, this._onSourcePrefChanged);
 
   // The element that we're inspecting, and the document that it comes from.
   this.viewedElement = null;
 
-  this._buildContextMenu();
   this.createStyleViews();
 
+  this._contextmenu = new StyleInspectorMenu(this, { isRuleView: false });
+
   // Add the tooltips and highlightersoverlay
   this.tooltips = new overlays.TooltipsOverlay(this);
   this.tooltips.addToView();
+
   this.highlighters = new overlays.HighlightersOverlay(this);
   this.highlighters.addToView();
 }
 
 /**
  * Memoized lookup of a l10n string from a string bundle.
  * @param {string} aName The key to lookup.
  * @returns A localized version of the given key.
  */
-CssHtmlTree.l10n = function CssHtmlTree_l10n(aName)
+CssComputedView.l10n = function CssComputedView_l10n(aName)
 {
   try {
-    return CssHtmlTree._strings.GetStringFromName(aName);
+    return CssComputedView._strings.GetStringFromName(aName);
   } catch (ex) {
     Services.console.logStringMessage("Error reading '" + aName + "'");
     throw new Error("l10n error with " + aName);
   }
 };
 
-XPCOMUtils.defineLazyGetter(CssHtmlTree, "_strings", function() {
+XPCOMUtils.defineLazyGetter(CssComputedView, "_strings", function() {
   return Services.strings.createBundle(
     "chrome://global/locale/devtools/styleinspector.properties");
 });
 
 XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
   return Cc["@mozilla.org/widget/clipboardhelper;1"]
          .getService(Ci.nsIClipboardHelper);
 });
 
-CssHtmlTree.prototype = {
+CssComputedView.prototype = {
   // Cache the list of properties that match the selected element.
   _matchedProperties: null,
 
   // Used for cancelling timeouts in the style filter.
   _filterChangedTimeout: null,
 
   // Holds the ID of the panelRefresh timeout.
   _panelRefreshTimeout: null,
@@ -259,17 +260,17 @@ CssHtmlTree.prototype = {
     if (this._computed && (data.pref == "devtools.defaultColorUnit" ||
         data.pref == PREF_ORIG_SOURCES)) {
       this.refreshPanel();
     }
   },
 
   /**
    * Update the view with a new selected element.
-   * The CssHtmlTree panel will show the style information for the given element.
+   * The CssComputedView panel will show the style information for the given element.
    * @param {NodeFront} aElement The highlighted node to get styles for.
    * @returns a promise that will be resolved when highlighting is complete.
    */
   selectElement: function(aElement) {
     if (!aElement) {
       this.viewedElement = null;
       this.noResults.hidden = false;
 
@@ -390,17 +391,17 @@ CssHtmlTree.prototype = {
 
     let deferred = promise.defer();
     this._createViewsPromise = deferred.promise;
 
     this.refreshSourceFilter();
     this.numVisibleProperties = 0;
     let fragment = this.styleDocument.createDocumentFragment();
 
-    this._createViewsProcess = new UpdateProcess(this.styleWindow, CssHtmlTree.propertyNames, {
+    this._createViewsProcess = new UpdateProcess(this.styleWindow, CssComputedView.propertyNames, {
       onItem: (aPropertyName) => {
         // Per-item callback.
         let propView = new PropertyView(this, aPropertyName);
         fragment.appendChild(propView.buildMain());
         fragment.appendChild(propView.buildSelectorContainer());
 
         if (propView.visible) {
           this.numVisibleProperties++;
@@ -420,17 +421,17 @@ CssHtmlTree.prototype = {
 
     this._createViewsProcess.schedule();
     return deferred.promise;
   },
 
   /**
    * Refresh the panel content.
    */
-  refreshPanel: function CssHtmlTree_refreshPanel()
+  refreshPanel: function CssComputedView_refreshPanel()
   {
     if (!this.viewedElement) {
       return promise.resolve();
     }
 
     // Capture the current viewed element to return from the promise handler
     // early if it changed
     let viewedElement = this.viewedElement;
@@ -586,65 +587,65 @@ CssHtmlTree.prototype = {
   },
 
   /**
    * When includeBrowserStylesCheckbox.checked is false we only display
    * properties that have matched selectors and have been included by the
    * document or one of thedocument's stylesheets. If .checked is false we
    * display all properties including those that come from UA stylesheets.
    */
-  refreshSourceFilter: function CssHtmlTree_setSourceFilter()
+  refreshSourceFilter: function CssComputedView_setSourceFilter()
   {
     this._matchedProperties = null;
     this._sourceFilter = this.includeBrowserStyles ?
                                  CssLogic.FILTER.UA :
                                  CssLogic.FILTER.USER;
   },
 
-  _updateSourceLinks: function CssHtmlTree__updateSourceLinks()
+  _onSourcePrefChanged: function CssComputedView__onSourcePrefChanged()
   {
     for (let propView of this.propertyViews) {
       propView.updateSourceLinks();
     }
     this.inspector.emit("computed-view-sourcelinks-updated");
   },
 
   /**
    * The CSS as displayed by the UI.
    */
-  createStyleViews: function CssHtmlTree_createStyleViews()
+  createStyleViews: function CssComputedView_createStyleViews()
   {
-    if (CssHtmlTree.propertyNames) {
+    if (CssComputedView.propertyNames) {
       return;
     }
 
-    CssHtmlTree.propertyNames = [];
+    CssComputedView.propertyNames = [];
 
     // Here we build and cache a list of css properties supported by the browser
     // We could use any element but let's use the main document's root element
     let styles = this.styleWindow.getComputedStyle(this.styleDocument.documentElement);
     let mozProps = [];
     for (let i = 0, numStyles = styles.length; i < numStyles; i++) {
       let prop = styles.item(i);
       if (prop.startsWith("--")) {
         // Skip any CSS variables used inside of browser CSS files
         continue;
       } else if (prop.startsWith("-")) {
         mozProps.push(prop);
       } else {
-        CssHtmlTree.propertyNames.push(prop);
+        CssComputedView.propertyNames.push(prop);
       }
     }
 
-    CssHtmlTree.propertyNames.sort();
-    CssHtmlTree.propertyNames.push.apply(CssHtmlTree.propertyNames,
+    CssComputedView.propertyNames.sort();
+    CssComputedView.propertyNames.push.apply(CssComputedView.propertyNames,
       mozProps.sort());
 
     this._createPropertyViews().then(null, e => {
-      if (!this.styleInspector) {
+      if (!this._isDestroyed) {
         console.warn("The creation of property views was cancelled because the " +
           "computed-view was destroyed before it was done creating views");
       } else {
         console.error(e);
       }
     });
   },
 
@@ -665,341 +666,106 @@ CssHtmlTree.prototype = {
    */
   focusWindow: function(aEvent)
   {
     let win = this.styleDocument.defaultView;
     win.focus();
   },
 
   /**
-   * Create a context menu.
-   */
-  _buildContextMenu: function()
-  {
-    let doc = this.styleDocument.defaultView.parent.document;
-
-    this._contextmenu = this.styleDocument.createElementNS(XUL_NS, "menupopup");
-    this._contextmenu.addEventListener("popupshowing", this._contextMenuUpdate);
-    this._contextmenu.id = "computed-view-context-menu";
-
-    // Select All
-    this.menuitemSelectAll = createMenuItem(this._contextmenu, {
-      label: "computedView.contextmenu.selectAll",
-      accesskey: "computedView.contextmenu.selectAll.accessKey",
-      command: this._onSelectAll
-    });
-
-    // Copy
-    this.menuitemCopy = createMenuItem(this._contextmenu, {
-      label: "computedView.contextmenu.copy",
-      accesskey: "computedView.contextmenu.copy.accessKey",
-      command: this._onCopy
-    });
-
-    // Copy color
-    this.menuitemCopyColor = createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.copyColor",
-      accesskey: "ruleView.contextmenu.copyColor.accessKey",
-      command: this._onCopyColor
-    });
-
-    // Copy URL
-    this.menuitemCopyUrl = createMenuItem(this._contextmenu, {
-      label: "styleinspector.contextmenu.copyUrl",
-      accesskey: "styleinspector.contextmenu.copyUrl.accessKey",
-      command: this._onCopyUrl
-    });
-
-    // Copy data URI
-    this.menuitemCopyImageDataUrl = createMenuItem(this._contextmenu, {
-      label: "styleinspector.contextmenu.copyImageDataUrl",
-      accesskey: "styleinspector.contextmenu.copyImageDataUrl.accessKey",
-      command: this._onCopyImageDataUrl
-    });
-
-    // Show Original Sources
-    this.menuitemSources= createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.showOrigSources",
-      accesskey: "ruleView.contextmenu.showOrigSources.accessKey",
-      command: this._onToggleOrigSources,
-      type: "checkbox"
-    });
-
-    let popupset = doc.documentElement.querySelector("popupset");
-    if (!popupset) {
-      popupset = doc.createElementNS(XUL_NS, "popupset");
-      doc.documentElement.appendChild(popupset);
-    }
-    popupset.appendChild(this._contextmenu);
-  },
-
-  /**
-   * Update the context menu. This means enabling or disabling menuitems as
-   * appropriate.
-   */
-  _contextMenuUpdate: function()
-  {
-    let win = this.styleDocument.defaultView;
-    let disable = win.getSelection().isCollapsed;
-    this.menuitemCopy.disabled = disable;
-
-    let showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
-    this.menuitemSources.setAttribute("checked", showOrig);
-
-    this.menuitemCopyColor.hidden = !this._isColorPopup();
-    this.menuitemCopyUrl.hidden = !this._isImageUrlPopup();
-    this.menuitemCopyImageDataUrl.hidden = !this._isImageUrlPopup();
-  },
-
-  /**
-   * A helper that determines if the popup was opened with a click to a color
-   * value and saves the color to this._colorToCopy.
-   *
-   * @return {Boolean}
-   *         true if click on color opened the popup, false otherwise.
-   */
-  _isColorPopup: function () {
-    this._colorToCopy = "";
-
-
-    let container = this._getPopupNodeContainer();
-    if (!container) {
-      return false;
-    }
-
-    let isColorNode = el => el.dataset && "color" in el.dataset;
-
-    while (!isColorNode(container)) {
-      container = container.parentNode;
-      if (!container) {
-        return false;
-      }
-    }
-
-    this._colorToCopy = container.dataset["color"];
-    return true;
-  },
-
-  /**
-   * Check if the context menu popup was opened with a click on an image link
-   * If true, save the image url to this._imageUrlToCopy
-   */
-  _isImageUrlPopup: function () {
-    this._imageUrlToCopy = "";
-
-    let container = this._getPopupNodeContainer();
-    let isImageUrlNode = this._isImageUrlNode(container);
-    if (isImageUrlNode) {
-      this._imageUrlToCopy = container.href;
-    }
-
-    return isImageUrlNode;
-  },
-
-  /**
-   * Check if a node is an image url
-   * @param {DOMNode} node The node which we want information about
-   * @return {Boolean} true if the node is an image url
-   */
-  _isImageUrlNode: function (node) {
-    let nodeInfo = this.getNodeInfo(node);
-    if (!nodeInfo) {
-      return false
-    }
-    return nodeInfo.type == overlays.VIEW_NODE_IMAGE_URL_TYPE;
-  },
-
-  /**
-   * Get the DOM Node container for the current popupNode.
-   * If popupNode is a textNode, return the parent node, otherwise return popupNode itself.
-   * @return {DOMNode}
-   */
-  _getPopupNodeContainer: function () {
-    let container = null;
-    let node = this.popupNode;
-
-    if (node) {
-      let isTextNode = node.nodeType == node.TEXT_NODE;
-      container = isTextNode ? node.parentElement : node;
-    }
-
-    return container;
-  },
-
-  /**
    * Context menu handler.
    */
   _onContextMenu: function(event) {
-    try {
-      this.popupNode = event.explicitOriginalTarget;
-      this.styleDocument.defaultView.focus();
-      this._contextmenu.openPopupAtScreen(event.screenX, event.screenY, true);
-    } catch(e) {
-      console.error(e);
-    }
-  },
-
-  /**
-   * Select all text.
-   */
-  _onSelectAll: function()
-  {
-    try {
-      let win = this.styleDocument.defaultView;
-      let selection = win.getSelection();
-
-      selection.selectAllChildren(this.styleDocument.documentElement);
-    } catch(e) {
-      console.error(e);
-    }
+    this._contextmenu.show(event);
   },
 
   _onClick: function(event) {
     let target = event.target;
 
     if (target.nodeName === "a") {
       event.stopPropagation();
       event.preventDefault();
       let browserWin = this.inspector.target.tab.ownerDocument.defaultView;
       browserWin.openUILinkIn(target.href, "tab");
     }
   },
 
-  _onCopyColor: function() {
-    clipboardHelper.copyString(this._colorToCopy);
-  },
-
   /**
-   * Retrieve the url for the selected image and copy it to the clipboard
+   * Callback for copy event. Copy selected text.
+   * @param {Event} event copy event object.
    */
-  _onCopyUrl: function() {
-    clipboardHelper.copyString(this._imageUrlToCopy);
+  _onCopy: function(event) {
+    this.copySelection();
+    if (event) {
+      event.preventDefault();
+    }
   },
 
   /**
-   * Retrieve the image data for the selected image url and copy it to the clipboard
+   * Copy the current selection to the clipboard
    */
-  _onCopyImageDataUrl: Task.async(function*() {
-    let message;
-    try {
-      let inspectorFront = this.inspector.inspector;
-      let data = yield inspectorFront.getImageDataFromURL(this._imageUrlToCopy);
-      message = yield data.data.string();
-    } catch (e) {
-      message = CssHtmlTree.l10n("styleinspector.copyImageDataUrlError");
-    }
-
-    clipboardHelper.copyString(message);
-  }),
-
-  /**
-   * Copy selected text.
-   *
-   * @param event The event object
-   */
-  _onCopy: function(event)
-  {
+  copySelection: function() {
     try {
       let win = this.styleDocument.defaultView;
       let text = win.getSelection().toString().trim();
 
       // Tidy up block headings by moving CSS property names and their values onto
       // the same line and inserting a colon between them.
       let textArray = text.split(/[\r\n]+/);
       let result = "";
 
       // Parse text array to output string.
       if (textArray.length > 1) {
         for (let prop of textArray) {
-          if (CssHtmlTree.propertyNames.indexOf(prop) !== -1) {
+          if (CssComputedView.propertyNames.indexOf(prop) !== -1) {
             // Property name
             result += prop;
           } else {
             // Property value
-            result += ": " + prop;
-            if (result.length > 0) {
-              result += ";\n";
-            }
+            result += ": " + prop + ";\n";
           }
         }
       } else {
         // Short text fragment.
         result = textArray[0];
       }
 
       clipboardHelper.copyString(result);
-
-      if (event) {
-        event.preventDefault();
-      }
     } catch(e) {
       console.error(e);
     }
   },
 
   /**
-   *  Toggle the original sources pref.
+   * Destructor for CssComputedView.
    */
-  _onToggleOrigSources: function()
-  {
-    let isEnabled = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
-    Services.prefs.setBoolPref(PREF_ORIG_SOURCES, !isEnabled);
-  },
-
-  /**
-   * Destructor for CssHtmlTree.
-   */
-  destroy: function CssHtmlTree_destroy()
+  destroy: function CssComputedView_destroy()
   {
     this.viewedElement = null;
     this._outputParser = null;
 
     gDevTools.off("pref-changed", this._handlePrefChange);
 
-    this._prefObserver.off(PREF_ORIG_SOURCES, this._updateSourceLinks);
+    this._prefObserver.off(PREF_ORIG_SOURCES, this._onSourcePrefChanged);
     this._prefObserver.destroy();
 
     // Cancel tree construction
     if (this._createViewsProcess) {
       this._createViewsProcess.cancel();
     }
     if (this._refreshProcess) {
       this._refreshProcess.cancel();
     }
 
     // Remove context menu
     if (this._contextmenu) {
-      // Destroy the Select All menuitem.
-      this.menuitemCopy.removeEventListener("command", this._onCopy);
-      this.menuitemCopy = null;
-
-      // Destroy the Copy menuitem.
-      this.menuitemSelectAll.removeEventListener("command", this._onSelectAll);
-      this.menuitemSelectAll = null;
-
-      // Destroy Copy Color menuitem.
-      this.menuitemCopyColor.removeEventListener("command", this._onCopyColor);
-      this.menuitemCopyColor = null;
-
-      // Destroy Copy URL menuitem
-      this.menuitemCopyUrl.removeEventListener("command", this._onCopyUrl);
-      this.menuitemCopyUrl = null;
-
-      // Destroy Copy Data URI menuitem.
-      this.menuitemCopyImageDataUrl.removeEventListener("command", this._onCopyImageDataUrl);
-      this.menuitemCopyImageDataUrl = null;
-
-      // Destroy the context menu.
-      this._contextmenu.removeEventListener("popupshowing", this._contextMenuUpdate);
-      this._contextmenu.parentNode.removeChild(this._contextmenu);
+      this._contextmenu.destroy();
       this._contextmenu = null;
     }
 
-    this.popupNode = null;
-
     this.tooltips.destroy();
     this.highlighters.destroy();
 
     // Remove bound listeners
     this.styleDocument.removeEventListener("mousedown", this.focusWindow);
     this.element.removeEventListener("click", this._onClick);
     this.element.removeEventListener("copy", this._onCopy);
     this.element.removeEventListener("contextmenu", this._onContextMenu);
@@ -1013,68 +779,48 @@ CssHtmlTree.prototype = {
     // Nodes used in templating
     this.root = null;
     this.element = null;
     this.panel = null;
     this.searchField = null;
     this.searchClearButton = null;
     this.includeBrowserStylesCheckbox = null;
 
-    // The document in which we display the results (csshtmltree.xul).
-    this.styleDocument = null;
-
-    for (let propView of this.propertyViews)  {
+    // Property views
+    for (let propView of this.propertyViews) {
       propView.destroy();
     }
+    this.propertyViews = null;
 
-    // The element that we're inspecting, and the document that it comes from.
-    this.propertyViews = null;
+    this.inspector = null;
+    this.styleDocument = null;
     this.styleWindow = null;
-    this.styleDocument = null;
-    this.styleInspector = null;
+
+    this._isDestroyed = true;
   }
 };
 
 function PropertyInfo(aTree, aName) {
   this.tree = aTree;
   this.name = aName;
 }
 PropertyInfo.prototype = {
   get value() {
     if (this.tree._computed) {
       let value = this.tree._computed[this.name].value;
       return value;
     }
   }
 };
 
-function createMenuItem(aMenu, aAttributes)
-{
-  let item = aMenu.ownerDocument.createElementNS(XUL_NS, "menuitem");
-
-  item.setAttribute("label", CssHtmlTree.l10n(aAttributes.label));
-  if (aAttributes.accesskey) {
-    item.setAttribute("accesskey", CssHtmlTree.l10n(aAttributes.accesskey));
-  }
-  item.addEventListener("command", aAttributes.command);
-
-  if (aAttributes.type) {
-    item.setAttribute("type", aAttributes.type);
-  }
-
-  aMenu.appendChild(item);
-
-  return item;
-}
-
 /**
  * A container to give easy access to property data from the template engine.
  *
  * @constructor
- * @param {CssHtmlTree} aTree the CssHtmlTree instance we are working with.
+ * @param {CssComputedView} aTree the CssComputedView instance we are working with.
  * @param {string} aName the CSS property name for which this PropertyView
  * instance will render the rules.
  */
 function PropertyView(aTree, aName)
 {
   this.tree = aTree;
   this.name = aName;
   this.getRTLAttr = aTree.getRTLAttr;
@@ -1388,17 +1134,16 @@ PropertyView.prototype = {
   {
     if (!this._matchedSelectorViews) {
       this._matchedSelectorViews = [];
       this._matchedSelectorResponse.forEach(
         function matchedSelectorViews_convert(aSelectorInfo) {
           this._matchedSelectorViews.push(new SelectorView(this.tree, aSelectorInfo));
         }, this);
     }
-
     return this._matchedSelectorViews;
   },
 
   /**
    * Update all the selector source links to reflect whether we're linking to
    * original sources (e.g. Sass files).
    */
   updateSourceLinks: function PropertyView_updateSourceLinks()
@@ -1457,17 +1202,17 @@ PropertyView.prototype = {
 
     this.valueNode.removeEventListener("click", this.onFocus, false);
     this.valueNode = null;
   }
 };
 
 /**
  * A container to give us easy access to display data from a CssRule
- * @param CssHtmlTree aTree, the owning CssHtmlTree
+ * @param CssComputedView aTree, the owning CssComputedView
  * @param aSelectorInfo
  */
 function SelectorView(aTree, aSelectorInfo)
 {
   this.tree = aTree;
   this.selectorInfo = aSelectorInfo;
   this._cacheStatusNames();
 
@@ -1504,17 +1249,17 @@ SelectorView.prototype = {
   {
     if (SelectorView.STATUS_NAMES.length) {
       return;
     }
 
     for (let status in CssLogic.STATUS) {
       let i = CssLogic.STATUS[status];
       if (i > CssLogic.STATUS.UNMATCHED) {
-        let value = CssHtmlTree.l10n("rule.status." + status);
+        let value = CssComputedView.l10n("rule.status." + status);
         // Replace normal spaces with non-breaking spaces
         SelectorView.STATUS_NAMES[i] = value.replace(/ /g, '\u00A0');
       }
     }
   },
 
   /**
    * A localized version of cssRule.status
@@ -1595,17 +1340,16 @@ SelectorView.prototype = {
   updateSource: function()
   {
     let rule = this.selectorInfo.rule;
     this.sheet = rule.parentStyleSheet;
 
     if (!rule || !this.sheet) {
       let oldSource = this.source;
       this.source = CssLogic.l10n("rule.sourceElement");
-      this.href = "#";
       return promise.resolve(oldSource);
     }
 
     let showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
 
     if (showOrig && rule.type != ELEMENT_STYLE) {
       let deferred = promise.defer();
 
@@ -1709,10 +1453,10 @@ function createChild(aParent, aTag, aAtt
         elt.setAttribute(attr, aAttributes[attr]);
       }
     }
   }
   aParent.appendChild(elt);
   return elt;
 }
 
-exports.CssHtmlTree = CssHtmlTree;
+exports.CssComputedView = CssComputedView;
 exports.PropertyView = PropertyView;
--- a/browser/devtools/styleinspector/moz.build
+++ b/browser/devtools/styleinspector/moz.build
@@ -6,11 +6,12 @@
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
 
 EXTRA_JS_MODULES.devtools.styleinspector += [
     'computed-view.js',
     'css-parsing-utils.js',
     'rule-view.js',
+    'style-inspector-menu.js',
     'style-inspector-overlays.js',
     'style-inspector.js',
 ]
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -1,15 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
-/* globals clipboardHelper, _strings, domUtils, AutocompletePopup */
+/* globals overlays, Services, EventEmitter, StyleInspectorMenu,
+   clipboardHelper, _strings, domUtils, AutocompletePopup */
 
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const {CssLogic} = require("devtools/styleinspector/css-logic");
 const {InplaceEditor, editableField, editableItem} =
       require("devtools/shared/inplace-editor");
@@ -20,20 +21,22 @@ const {PrefObserver, PREF_ORIG_SOURCES} 
 const {
   parseDeclarations,
   parseSingleValue,
   parsePseudoClassesAndAttributes,
   SELECTOR_ATTRIBUTE,
   SELECTOR_ELEMENT,
   SELECTOR_PSEUDO_CLASS
 } = require("devtools/styleinspector/css-parsing-utils");
-const overlays = require("devtools/styleinspector/style-inspector-overlays");
-const EventEmitter = require("devtools/toolkit/event-emitter");
-
-Cu.import("resource://gre/modules/Services.jsm");
+
+loader.lazyRequireGetter(this, "overlays", "devtools/styleinspector/style-inspector-overlays");
+loader.lazyRequireGetter(this, "EventEmitter", "devtools/toolkit/event-emitter");
+loader.lazyRequireGetter(this, "StyleInspectorMenu", "devtools/styleinspector/style-inspector-menu");
+loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
 const PREF_DEFAULT_COLOR_UNIT = "devtools.defaultColorUnit";
 const PREF_ENABLE_MDN_DOCS_TOOLTIP =
       "devtools.inspector.mdnDocsTooltip.enabled";
@@ -1136,76 +1139,61 @@ TextProperty.prototype = {
  *   Can mark a property disabled or enabled.
  */
 
 /**
  * CssRuleView is a view of the style rules and declarations that
  * apply to a given element.  After construction, the 'element'
  * property will be available with the user interface.
  *
- * @param {Inspector} aInspector
- * @param {Document} aDoc
- *        The document that will contain the rule view.
+ * @param {Inspector} inspector toolbox panel
+ * @param {Document} document The document that will contain the rule view.
  * @param {object} aStore
  *        The CSS rule view can use this object to store metadata
  *        that might outlast the rule view, particularly the current
  *        set of disabled properties.
  * @param {PageStyleFront} aPageStyle
  *        The PageStyleFront for communicating with the remote server.
  * @constructor
  */
-function CssRuleView(aInspector, aDoc, aStore, aPageStyle) {
-  this.inspector = aInspector;
-  this.doc = aDoc;
+function CssRuleView(inspector, document, aStore, aPageStyle) {
+  this.inspector = inspector;
+  this.styleDocument = document;
+  this.styleWindow = this.styleDocument.defaultView;
   this.store = aStore || {};
   this.pageStyle = aPageStyle;
 
   this._editorsExpandedForFilter = [];
   this._outputParser = new OutputParser();
 
-  this._buildContextMenu = this._buildContextMenu.bind(this);
-  this._onContextMenu = this._onContextMenu.bind(this);
-  this._contextMenuUpdate = this._contextMenuUpdate.bind(this);
   this._onKeypress = this._onKeypress.bind(this);
   this._onAddRule = this._onAddRule.bind(this);
-  this._onSelectAll = this._onSelectAll.bind(this);
+  this._onContextMenu = this._onContextMenu.bind(this);
   this._onCopy = this._onCopy.bind(this);
-  this._onCopyColor = this._onCopyColor.bind(this);
-  this._onCopyUrl = this._onCopyUrl.bind(this);
-  this._onCopyImageDataUrl = this._onCopyImageDataUrl.bind(this);
-  this._onCopyLocation = this._onCopyLocation.bind(this);
-  this._onCopyPropertyDeclaration = this._onCopyPropertyDeclaration.bind(this);
-  this._onCopyPropertyName = this._onCopyPropertyName.bind(this);
-  this._onCopyPropertyValue = this._onCopyPropertyValue.bind(this);
-  this._onCopyRule = this._onCopyRule.bind(this);
-  this._onCopySelector = this._onCopySelector.bind(this);
-  this._onToggleOrigSources = this._onToggleOrigSources.bind(this);
-  this._onShowMdnDocs = this._onShowMdnDocs.bind(this);
   this._onFilterStyles = this._onFilterStyles.bind(this);
   this._onFilterKeyPress = this._onFilterKeyPress.bind(this);
   this._onClearSearch = this._onClearSearch.bind(this);
-  this._onFilterTextboxContextMenu =
-    this._onFilterTextboxContextMenu.bind(this);
+  this._onFilterTextboxContextMenu = this._onFilterTextboxContextMenu.bind(this);
   this._onTogglePseudoClassPanel = this._onTogglePseudoClassPanel.bind(this);
   this._onTogglePseudoClass = this._onTogglePseudoClass.bind(this);
 
-  this.element = this.doc.getElementById("ruleview-container");
-  this.addRuleButton = this.doc.getElementById("ruleview-add-rule-button");
-  this.searchField = this.doc.getElementById("ruleview-searchbox");
-  this.searchClearButton =
-    this.doc.getElementById("ruleview-searchinput-clear");
-  this.pseudoClassPanel = this.doc.getElementById("pseudo-class-panel");
-  this.pseudoClassToggle = this.doc.getElementById("pseudo-class-panel-toggle");
-  this.hoverCheckbox = this.doc.getElementById("pseudo-hover-toggle");
-  this.activeCheckbox = this.doc.getElementById("pseudo-active-toggle");
-  this.focusCheckbox = this.doc.getElementById("pseudo-focus-toggle");
+  let doc = this.styleDocument;
+  this.element = doc.getElementById("ruleview-container");
+  this.addRuleButton = doc.getElementById("ruleview-add-rule-button");
+  this.searchField = doc.getElementById("ruleview-searchbox");
+  this.searchClearButton = doc.getElementById("ruleview-searchinput-clear");
+  this.pseudoClassPanel = doc.getElementById("pseudo-class-panel");
+  this.pseudoClassToggle = doc.getElementById("pseudo-class-panel-toggle");
+  this.hoverCheckbox = doc.getElementById("pseudo-hover-toggle");
+  this.activeCheckbox = doc.getElementById("pseudo-active-toggle");
+  this.focusCheckbox = doc.getElementById("pseudo-focus-toggle");
 
   this.searchClearButton.hidden = true;
 
-  this.doc.addEventListener("keypress", this._onKeypress);
+  this.styleDocument.addEventListener("keypress", this._onKeypress);
   this.element.addEventListener("copy", this._onCopy);
   this.element.addEventListener("contextmenu", this._onContextMenu);
   this.addRuleButton.addEventListener("click", this._onAddRule);
   this.searchField.addEventListener("input", this._onFilterStyles);
   this.searchField.addEventListener("keypress", this._onFilterKeyPress);
   this.searchField.addEventListener("contextmenu",
                                     this._onFilterTextboxContextMenu);
   this.searchClearButton.addEventListener("click", this._onClearSearch);
@@ -1227,21 +1215,22 @@ function CssRuleView(aInspector, aDoc, a
   this.showUserAgentStyles = Services.prefs.getBoolPref(PREF_UA_STYLES);
   this.enableMdnDocsTooltip =
     Services.prefs.getBoolPref(PREF_ENABLE_MDN_DOCS_TOOLTIP);
 
   let options = {
     autoSelect: true,
     theme: "auto"
   };
-  this.popup = new AutocompletePopup(aDoc.defaultView.parent.document, options);
-
-  this._buildContextMenu();
+  this.popup = new AutocompletePopup(this.styleWindow.parent.document, options);
+
   this._showEmpty();
 
+  this._contextmenu = new StyleInspectorMenu(this, { isRuleView: true });
+
   // Add the tooltips and highlighters to the view
   this.tooltips = new overlays.TooltipsOverlay(this);
   this.tooltips.addToView();
   this.highlighters = new overlays.HighlightersOverlay(this);
   this.highlighters.addToView();
 
   EventEmitter.decorate(this);
 }
@@ -1251,118 +1240,16 @@ exports.CssRuleView = CssRuleView;
 CssRuleView.prototype = {
   // The element that we're inspecting.
   _viewedElement: null,
 
   // Used for cancelling timeouts in the style filter.
   _filterChangedTimeout: null,
 
   /**
-   * Build the context menu.
-   */
-  _buildContextMenu: function() {
-    let doc = this.doc.defaultView.parent.document;
-
-    this._contextmenu = doc.createElementNS(XUL_NS, "menupopup");
-    this._contextmenu.addEventListener("popupshowing", this._contextMenuUpdate);
-    this._contextmenu.id = "rule-view-context-menu";
-
-    this.menuitemCopy = createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.copy",
-      accesskey: "ruleView.contextmenu.copy.accessKey",
-      command: this._onCopy
-    });
-
-    this.menuitemCopyLocation = createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.copyLocation",
-      command: this._onCopyLocation
-    });
-
-    this.menuitemCopyRule = createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.copyRule",
-      command: this._onCopyRule
-    });
-
-    this.menuitemCopyColor = createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.copyColor",
-      accesskey: "ruleView.contextmenu.copyColor.accessKey",
-      command: this._onCopyColor
-    });
-
-    this.menuitemCopyUrl = createMenuItem(this._contextmenu, {
-      label: "styleinspector.contextmenu.copyUrl",
-      accesskey: "styleinspector.contextmenu.copyUrl.accessKey",
-      command: this._onCopyUrl
-    });
-
-    this.menuitemCopyImageDataUrl = createMenuItem(this._contextmenu, {
-      label: "styleinspector.contextmenu.copyImageDataUrl",
-      accesskey: "styleinspector.contextmenu.copyImageDataUrl.accessKey",
-      command: this._onCopyImageDataUrl
-    });
-
-    this.menuitemCopyPropertyDeclaration = createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.copyPropertyDeclaration",
-      command: this._onCopyPropertyDeclaration
-    });
-
-    this.menuitemCopyPropertyName = createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.copyPropertyName",
-      command: this._onCopyPropertyName
-    });
-
-    this.menuitemCopyPropertyValue = createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.copyPropertyValue",
-      command: this._onCopyPropertyValue
-    });
-
-    this.menuitemCopySelector = createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.copySelector",
-      command: this._onCopySelector
-    });
-
-    createMenuSeparator(this._contextmenu);
-
-    this.menuitemSelectAll = createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.selectAll",
-      accesskey: "ruleView.contextmenu.selectAll.accessKey",
-      command: this._onSelectAll
-    });
-
-    createMenuSeparator(this._contextmenu);
-
-    this.menuitemAddRule = createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.addNewRule",
-      accesskey: "ruleView.contextmenu.addNewRule.accessKey",
-      command: this._onAddRule
-    });
-
-    this.menuitemSources = createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.showOrigSources",
-      accesskey: "ruleView.contextmenu.showOrigSources.accessKey",
-      command: this._onToggleOrigSources,
-      type: "checkbox"
-    });
-
-    this.menuitemShowMdnDocs = createMenuItem(this._contextmenu, {
-      label: "ruleView.contextmenu.showMdnDocs",
-      accesskey: "ruleView.contextmenu.showMdnDocs.accessKey",
-      command: this._onShowMdnDocs
-    });
-
-    let popupset = doc.documentElement.querySelector("popupset");
-    if (!popupset) {
-      popupset = doc.createElementNS(XUL_NS, "popupset");
-      doc.documentElement.appendChild(popupset);
-    }
-
-    popupset.appendChild(this._contextmenu);
-  },
-
-  /**
    * Get an instance of SelectorHighlighter (used to highlight nodes that match
    * selectors in the rule-view). A new instance is only created the first time
    * this function is called. The same instance will then be returned.
    * @return {Promise} Resolves to the instance of the highlighter.
    */
   getSelectorHighlighter: Task.async(function*() {
     let utils = this.inspector.toolbox.highlighterUtils;
     if (!utils.supportsCustomHighlighters()) {
@@ -1438,88 +1325,16 @@ CssRuleView.prototype = {
     if (!highlighter) {
       return;
     }
 
     yield highlighter.hide();
   }),
 
   /**
-   * Update the context menu. This means enabling or disabling menuitems as
-   * appropriate.
-   */
-  _contextMenuUpdate: function() {
-    this._enableCopyMenuItems(this.doc.popupNode.parentNode);
-
-    this.menuitemAddRule.disabled = this.inspector.selection.isAnonymousNode();
-
-    this.menuitemShowMdnDocs.hidden = !this.enableMdnDocsTooltip ||
-                                      !this.doc.popupNode.parentNode
-                                      .classList.contains(PROPERTY_NAME_CLASS);
-
-    let showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
-    this.menuitemSources.setAttribute("checked", showOrig);
-  },
-
-  /**
-   * Display the necessary copy context menu items depending on the clicked
-   * node and selection in the rule view.
-   */
-  _enableCopyMenuItems: function(target) {
-    let win = this.doc.defaultView;
-
-    // Copy selection.
-    let selection = win.getSelection();
-    let copy;
-
-    if (selection.toString()) {
-      // Panel text selected
-      copy = true;
-    } else if (selection.anchorNode) {
-      // input type="text"
-      let { selectionStart, selectionEnd } = this.doc.popupNode;
-
-      if (isFinite(selectionStart) && isFinite(selectionEnd) &&
-          selectionStart !== selectionEnd) {
-        copy = true;
-      }
-    } else {
-      // No text selected, disable copy.
-      copy = false;
-    }
-
-    this.menuitemCopy.hidden = !copy;
-    this.menuitemCopyColor.hidden = !this._isColorPopup();
-    this.menuitemCopyUrl.hidden = !this._isImageUrlPopup();
-    this.menuitemCopyImageDataUrl.hidden = !this._isImageUrlPopup();
-
-    this.menuitemCopyLocation.hidden = true;
-    this.menuitemCopyPropertyDeclaration.hidden = true;
-    this.menuitemCopyPropertyName.hidden = true;
-    this.menuitemCopyPropertyValue.hidden = true;
-    this.menuitemCopySelector.hidden = true;
-
-    this._clickedNodeInfo = this.getNodeInfo(target);
-
-    if (!this._clickedNodeInfo) {
-      return;
-    } else if (this._clickedNodeInfo.type == overlays.VIEW_NODE_PROPERTY_TYPE) {
-      this.menuitemCopyPropertyDeclaration.hidden = false;
-      this.menuitemCopyPropertyName.hidden = false;
-    } else if (this._clickedNodeInfo.type == overlays.VIEW_NODE_VALUE_TYPE) {
-      this.menuitemCopyPropertyDeclaration.hidden = false;
-      this.menuitemCopyPropertyValue.hidden = false;
-    } else if (this._clickedNodeInfo.type == overlays.VIEW_NODE_SELECTOR_TYPE) {
-      this.menuitemCopySelector.hidden = false;
-    } else if (this._clickedNodeInfo.type == overlays.VIEW_NODE_LOCATION_TYPE) {
-      this.menuitemCopyLocation.hidden = false;
-    }
-  },
-
-  /**
    * Get the type of a given node in the rule-view
    * @param {DOMNode} node The node which we want information about
    * @return {Object} The type information object contains the following props:
    * - type {String} One of the VIEW_NODE_XXX_TYPE const in
    *   style-inspector-overlays
    * - value {Object} Depends on the type of the node
    * returns null of the node isn't anything we care about
    */
@@ -1570,287 +1385,92 @@ CssRuleView.prototype = {
     } else if (classes.contains("ruleview-selector-unmatched") ||
                classes.contains("ruleview-selector-matched") ||
                classes.contains("ruleview-selectorcontainer") ||
                classes.contains("ruleview-selector") ||
                classes.contains("ruleview-selector-attribute") ||
                classes.contains("ruleview-selector-pseudo-class") ||
                classes.contains("ruleview-selector-pseudo-class-lock")) {
       type = overlays.VIEW_NODE_SELECTOR_TYPE;
-      value = node.offsetParent._ruleEditor.selectorText.textContent;
-    } else if (classes.contains("ruleview-rule-source")) {
+      value = this._getRuleEditorForNode(node).selectorText.textContent;
+    } else if (classes.contains("ruleview-rule-source") ||
+               classes.contains("ruleview-rule-source-label")) {
       type = overlays.VIEW_NODE_LOCATION_TYPE;
-      let ruleEditor = node.offsetParent._ruleEditor;
-      let rule = ruleEditor.rule;
+      let rule = this._getRuleEditorForNode(node).rule;
       value = (rule.sheet && rule.sheet.href) ? rule.sheet.href : rule.title;
     } else {
       return null;
     }
 
     return {type, value};
   },
 
   /**
-   * A helper that determines if the popup was opened with a click to a color
-   * value and saves the color to this._colorToCopy.
-   *
-   * @return {Boolean}
-   *         true if click on color opened the popup, false otherwise.
-   */
-  _isColorPopup: function() {
-    this._colorToCopy = "";
-
-    let container = this._getPopupNodeContainer();
-    if (!container) {
-      return false;
-    }
-
-    let isColorNode = el => el.dataset && "color" in el.dataset;
-
-    while (!isColorNode(container)) {
-      container = container.parentNode;
-      if (!container) {
-        return false;
-      }
-    }
-
-    this._colorToCopy = container.dataset.color;
-    return true;
-  },
-
-  /**
-   * Check if the context menu popup was opened with a click on an image link
-   * If true, save the image url to this._imageUrlToCopy
+   * Retrieve the RuleEditor instance that should be stored on
+   * the offset parent of the node
    */
-  _isImageUrlPopup: function() {
-    this._imageUrlToCopy = "";
-
-    let container = this._getPopupNodeContainer();
-    let isImageUrlNode = this._isImageUrlNode(container);
-    if (isImageUrlNode) {
-      this._imageUrlToCopy = container.href;
+  _getRuleEditorForNode: function(node) {
+    if (!node.offsetParent) {
+      // some nodes don't have an offsetParent, but their parentNode does
+      node = node.parentNode;
     }
-
-    return isImageUrlNode;
-  },
-
-  /**
-   * Check if a node is an image url
-   * @param {DOMNode} node The node which we want information about
-   * @return {Boolean} true if the node is an image url
-   */
-  _isImageUrlNode: function(node) {
-    let nodeInfo = this.getNodeInfo(node);
-    if (!nodeInfo) {
-      return false;
-    }
-    return nodeInfo.type == overlays.VIEW_NODE_IMAGE_URL_TYPE;
-  },
-
-  /**
-   * Get the DOM Node container for the current popupNode.
-   * If popupNode is a textNode, return the parent node, otherwise
-  *  return popupNode itself.
-   * @return {DOMNode}
-   */
-  _getPopupNodeContainer: function() {
-    let container = null;
-    let node = this.doc.popupNode;
-
-    if (node) {
-      let isTextNode = node.nodeType == node.TEXT_NODE;
-      container = isTextNode ? node.parentElement : node;
-    }
-
-    return container;
+    return node.offsetParent._ruleEditor;
   },
 
   /**
    * Context menu handler.
    */
   _onContextMenu: function(event) {
-    try {
-      // In the sidebar we do not have this.doc.popupNode so we need to save
-      // the node ourselves.
-      this.doc.popupNode = event.explicitOriginalTarget;
-      this.doc.defaultView.focus();
-      this._contextmenu.openPopupAtScreen(event.screenX, event.screenY, true);
-    } catch(e) {
-      console.error(e);
+    this._contextmenu.show(event);
+  },
+
+  /**
+   * Callback for copy event. Copy the selected text.
+   * @param {Event} event copy event object.
+   */
+  _onCopy: function(event) {
+    if (event) {
+      this.copySelection(event.target);
+      event.preventDefault();
     }
   },
 
   /**
-   * Select all text.
+   * Copy the current selection. The current target is necessary
+   * if the selection is inside an input or a textarea
+   * @param {DOMNode} target DOMNode target of the copy action
    */
-  _onSelectAll: function() {
-    let win = this.doc.defaultView;
-    let selection = win.getSelection();
-
-    selection.selectAllChildren(this.doc.documentElement);
-  },
-
-  /**
-   * Copy selected text from the rule view.
-   *
-   * @param {Event} event
-   *        The event object.
-   */
-  _onCopy: function(event) {
+  copySelection: function(target) {
     try {
-      let target = event.target;
-      let text;
-
-      if (event.target.nodeName === "menuitem") {
-        target = this.doc.popupNode;
-      }
-
-      if (target.nodeName == "input") {
+      let text = "";
+
+      if (target && target.nodeName == "input") {
         let start = Math.min(target.selectionStart, target.selectionEnd);
         let end = Math.max(target.selectionStart, target.selectionEnd);
         let count = end - start;
         text = target.value.substr(start, count);
       } else {
-        let win = this.doc.defaultView;
-        let selection = win.getSelection();
-
-        text = selection.toString();
+        text = this.styleWindow.getSelection().toString();
 
         // Remove any double newlines.
         text = text.replace(/(\r?\n)\r?\n/g, "$1");
 
         // Remove "inline"
         let inline = _strings.GetStringFromName("rule.sourceInline");
         let rx = new RegExp("^" + inline + "\\r?\\n?", "g");
         text = text.replace(rx, "");
       }
 
       clipboardHelper.copyString(text);
-      event.preventDefault();
     } catch(e) {
       console.error(e);
     }
   },
 
   /**
-   * Copy the most recently selected color value to clipboard.
-   */
-  _onCopyColor: function() {
-    clipboardHelper.copyString(this._colorToCopy);
-  },
-
-  /**
-   * Retrieve the url for the selected image and copy it to the clipboard
-   */
-  _onCopyUrl: function() {
-    clipboardHelper.copyString(this._imageUrlToCopy);
-  },
-
-  /**
-   * Retrieve the image data for the selected image url and copy it to
-   * the clipboard
-   */
-  _onCopyImageDataUrl: Task.async(function*() {
-    let message;
-    try {
-      let inspectorFront = this.inspector.inspector;
-      let data = yield inspectorFront.getImageDataFromURL(this._imageUrlToCopy);
-      message = yield data.data.string();
-    } catch (e) {
-      message =
-        _strings.GetStringFromName("styleinspector.copyImageDataUrlError");
-    }
-
-    clipboardHelper.copyString(message);
-  }),
-
-  /**
-   * Copy the rule source location of the current clicked node.
-   */
-  _onCopyLocation: function() {
-    if (!this._clickedNodeInfo) {
-      return;
-    }
-
-    clipboardHelper.copyString(this._clickedNodeInfo.value, this.doc);
-  },
-
-  /**
-   * Copy the rule property declaration of the current clicked node.
-   */
-  _onCopyPropertyDeclaration: function() {
-    if (!this._clickedNodeInfo) {
-      return;
-    }
-
-    let textProp = this._clickedNodeInfo.value.textProperty;
-    clipboardHelper.copyString(textProp.stringifyProperty(), this.doc);
-  },
-
-  /**
-   * Copy the rule property name of the current clicked node.
-   */
-  _onCopyPropertyName: function() {
-    if (!this._clickedNodeInfo) {
-      return;
-    }
-
-    clipboardHelper.copyString(this._clickedNodeInfo.value.property, this.doc);
-  },
-
-  /**
-   * Copy the rule property value of the current clicked node.
-   */
-  _onCopyPropertyValue: function() {
-    if (!this._clickedNodeInfo) {
-      return;
-    }
-
-    clipboardHelper.copyString(this._clickedNodeInfo.value.value, this.doc);
-  },
-
-  /**
-   * Copy the rule of the current clicked node.
-   */
-  _onCopyRule: function() {
-    let ruleEditor = this.doc.popupNode.parentNode.offsetParent._ruleEditor;
-    let rule = ruleEditor.rule;
-    clipboardHelper.copyString(rule.stringifyRule(), this.doc);
-  },
-
-  /**
-   * Copy the rule selector of the current clicked node.
-   */
-  _onCopySelector: function() {
-    if (!this._clickedNodeInfo) {
-      return;
-    }
-
-    clipboardHelper.copyString(this._clickedNodeInfo.value, this.doc);
-  },
-
-  /**
-   *  Toggle the original sources pref.
-   */
-  _onToggleOrigSources: function() {
-    let isEnabled = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
-    Services.prefs.setBoolPref(PREF_ORIG_SOURCES, !isEnabled);
-  },
-
-  /**
-   *  Show docs from MDN for a CSS property.
-   */
-  _onShowMdnDocs: function() {
-    let cssPropertyName = this.doc.popupNode.textContent;
-    let anchor = this.doc.popupNode.parentNode;
-    let cssDocsTooltip = this.tooltips.cssDocs;
-    cssDocsTooltip.show(anchor, cssPropertyName);
-  },
-
-  /**
    * Add a new rule to the current element.
    */
   _onAddRule: function() {
     let elementStyle = this._elementStyle;
     let element = elementStyle.element;
     let rules = elementStyle.rules;
     let client = this.inspector.toolbox._target.client;
     let pseudoClasses = element.pseudoClassLocks;
@@ -1858,16 +1478,17 @@ CssRuleView.prototype = {
     if (!client.traits.addNewRule) {
       return;
     }
 
     this.pageStyle.addNewRule(element, pseudoClasses).then(options => {
       let newRule = new Rule(elementStyle, options);
       rules.push(newRule);
       let editor = new RuleEditor(this, newRule);
+      newRule.editor = editor;
 
       // Insert the new rule editor after the inline element rule
       if (rules.length <= 1) {
         this.element.appendChild(editor.element);
       } else {
         for (let rule of rules) {
           if (rule.domRule.type === ELEMENT_STYLE) {
             let referenceElement = rule.editor.element.nextSibling;
@@ -1905,36 +1526,29 @@ CssRuleView.prototype = {
       || this.tooltips.isEditing;
   },
 
   _handlePrefChange: function(pref) {
     if (pref === PREF_UA_STYLES) {
       this.showUserAgentStyles = Services.prefs.getBoolPref(pref);
     }
 
-    if (pref === PREF_ENABLE_MDN_DOCS_TOOLTIP) {
-      this.enableMdnDocsTooltip = Services.prefs.getBoolPref(pref);
-    }
-
     // Reselect the currently selected element
     let refreshOnPrefs = [PREF_UA_STYLES, PREF_DEFAULT_COLOR_UNIT];
     if (refreshOnPrefs.indexOf(pref) > -1) {
       let element = this._viewedElement;
       this._viewedElement = null;
       this.selectElement(element);
     }
   },
 
+  /**
+   * Update source links when pref for showing original sources changes
+   */
   _onSourcePrefChanged: function() {
-    if (this.menuitemSources) {
-      let isEnabled = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
-      this.menuitemSources.setAttribute("checked", isEnabled);
-    }
-
-    // update text of source links if the rule-view is populated
     if (this._elementStyle && this._elementStyle.rules) {
       for (let rule of this._elementStyle.rules) {
         if (rule.editor) {
           rule.editor.updateSourceLink();
         }
       }
       this.inspector.emit("rule-view-sourcelinks-updated");
     }
@@ -1981,17 +1595,17 @@ CssRuleView.prototype = {
     }
   },
 
   /**
    * Context menu handler for filter style search box.
    */
   _onFilterTextboxContextMenu: function(event) {
     try {
-      this.doc.defaultView.focus();
+      this.styleWindow.focus();
       let contextmenu = this.inspector.toolbox.textboxContextMenuPopup;
       contextmenu.openPopupAtScreen(event.screenX, event.screenY, true);
     } catch(e) {
       console.error(e);
     }
   },
 
   /**
@@ -2013,91 +1627,27 @@ CssRuleView.prototype = {
     this.isDestroyed = true;
     this.clear();
 
     gDummyPromise = null;
 
     this._prefObserver.off(PREF_ORIG_SOURCES, this._onSourcePrefChanged);
     this._prefObserver.off(PREF_UA_STYLES, this._handlePrefChange);
     this._prefObserver.off(PREF_DEFAULT_COLOR_UNIT, this._handlePrefChange);
-    this._prefObserver.off(PREF_ENABLE_MDN_DOCS_TOOLTIP,
-                           this._handlePrefChange);
     this._prefObserver.destroy();
 
     this._outputParser = null;
     this._editorsExpandedForFilter = null;
 
     // Remove context menu
     if (this._contextmenu) {
-      // Destroy the Add Rule menuitem.
-      this.menuitemAddRule.removeEventListener("command", this._onAddRule);
-      this.menuitemAddRule = null;
-
-      // Destroy the Select All menuitem.
-      this.menuitemSelectAll.removeEventListener("command", this._onSelectAll);
-      this.menuitemSelectAll = null;
-
-      // Destroy the Copy menuitem.
-      this.menuitemCopy.removeEventListener("command", this._onCopy);
-      this.menuitemCopy = null;
-
-      // Destroy Copy Color menuitem.
-      this.menuitemCopyColor.removeEventListener("command", this._onCopyColor);
-      this.menuitemCopyColor = null;
-
-      // Destroy Copy URL menuitem.
-      this.menuitemCopyUrl.removeEventListener("command",
-        this._onCopyUrl);
-      this.menuitemCopyUrl = null;
-
-      // Destroy Copy Data URI menuitem.
-      this.menuitemCopyImageDataUrl.removeEventListener("command",
-        this._onCopyImageDataUrl);
-      this.menuitemCopyImageDataUrl = null;
-
-      this.menuitemCopyLocation.removeEventListener("command",
-        this._onCopyLocation);
-      this.menuitemCopyLocation = null;
-
-      this.menuitemCopyPropertyDeclaration.removeEventListener("command",
-        this._onCopyPropertyDeclaration);
-      this.menuitemCopyPropertyDeclaration = null;
-
-      this.menuitemCopyPropertyName.removeEventListener("command",
-        this._onCopyPropertyName);
-      this.menuitemCopyPropertyName = null;
-
-      this.menuitemCopyPropertyValue.removeEventListener("command",
-        this._onCopyPropertyValue);
-      this.menuitemCopyPropertyValue = null;
-
-      this.menuitemCopyRule.removeEventListener("command",
-        this._onCopyRule);
-      this.menuitemCopyRule = null;
-
-      this.menuitemCopySelector.removeEventListener("command",
-        this._onCopySelector);
-      this.menuitemCopySelector = null;
-
-      this.menuitemSources.removeEventListener("command",
-        this._onToggleOrigSources);
-      this.menuitemSources = null;
-
-      this._clickedNodeInfo = null;
-
-      // Destroy the context menu.
-      this._contextmenu.removeEventListener("popupshowing",
-        this._contextMenuUpdate);
-      this._contextmenu.parentNode.removeChild(this._contextmenu);
+      this._contextmenu.destroy();
       this._contextmenu = null;
     }
 
-    // We manage the popupNode ourselves so we also need to destroy it.
-    this.doc.popupNode = null;
-
     this.tooltips.destroy();
     this.highlighters.destroy();
 
     // Remove bound listeners
     this.element.removeEventListener("copy", this._onCopy);
     this.element.removeEventListener("contextmenu", this._onContextMenu);
     this.addRuleButton.removeEventListener("click", this._onAddRule);
     this.searchField.removeEventListener("input", this._onFilterStyles);
@@ -2114,16 +1664,20 @@ CssRuleView.prototype = {
     this.searchField = null;
     this.searchClearButton = null;
     this.pseudoClassPanel = null;
     this.pseudoClassToggle = null;
     this.hoverCheckbox = null;
     this.activeCheckbox = null;
     this.focusCheckbox = null;
 
+    this.inspector = null;
+    this.styleDocument = null;
+    this.styleWindow = null;
+
     if (this.element.parentNode) {
       this.element.parentNode.removeChild(this.element);
     }
 
     if (this._elementStyle) {
       this._elementStyle.destroy();
     }
 
@@ -2248,17 +1802,17 @@ CssRuleView.prototype = {
       this.emit("ruleview-refreshed");
     }).then(null, promiseWarn);
   },
 
   /**
    * Show the user that the rule view has no node selected.
    */
   _showEmpty: function() {
-    if (this.doc.getElementById("noResults") > 0) {
+    if (this.styleDocument.getElementById("noResults") > 0) {
       return;
     }
 
     createChild(this.element, "div", {
       id: "noResults",
       textContent: CssLogic.l10n("rule.empty")
     });
   },
@@ -2328,29 +1882,29 @@ CssRuleView.prototype = {
   /**
    * Creates an expandable container in the rule view
    * @param  {String}  aLabel The label for the container header
    * @param  {Boolean} isPseudo Whether or not the container will hold
    *                            pseudo element rules
    * @return {DOMNode} The container element
    */
   createExpandableContainer: function(aLabel, isPseudo = false) {
-    let header = this.doc.createElementNS(HTML_NS, "div");
+    let header = this.styleDocument.createElementNS(HTML_NS, "div");
     header.className = this._getRuleViewHeaderClassName(true);
     header.classList.add("show-expandable-container");
     header.textContent = aLabel;
 
-    let twisty = this.doc.createElementNS(HTML_NS, "span");
+    let twisty = this.styleDocument.createElementNS(HTML_NS, "span");
     twisty.className = "ruleview-expander theme-twisty";
     twisty.setAttribute("open", "true");
 
     header.insertBefore(twisty, header.firstChild);
     this.element.appendChild(header);
 
-    let container = this.doc.createElementNS(HTML_NS, "div");
+    let container = this.styleDocument.createElementNS(HTML_NS, "div");
     container.classList.add("ruleview-expandable-container");
     this.element.appendChild(container);
 
     let toggleContainerVisibility = (isPseudo, showPseudo) => {
       let isOpen = twisty.getAttribute("open");
 
       if (isPseudo) {
         this._showPseudoElements = !!showPseudo;
@@ -2429,25 +1983,25 @@ CssRuleView.prototype = {
         } else if (rule.domRule.type !== ELEMENT_STYLE) {
           continue;
         }
       }
 
       // Only print header for this element if there are pseudo elements
       if (seenPseudoElement && !seenNormalElement && !rule.pseudoElement) {
         seenNormalElement = true;
-        let div = this.doc.createElementNS(HTML_NS, "div");
+        let div = this.styleDocument.createElementNS(HTML_NS, "div");
         div.className = this._getRuleViewHeaderClassName();
         div.textContent = this.selectedElementLabel;
         this.element.appendChild(div);
       }
 
       let inheritedSource = rule.inheritedSource;
       if (inheritedSource && inheritedSource != lastInheritedSource) {
-        let div = this.doc.createElementNS(HTML_NS, "div");
+        let div = this.styleDocument.createElementNS(HTML_NS, "div");
         div.className = this._getRuleViewHeaderClassName();
         div.textContent = inheritedSource;
         lastInheritedSource = inheritedSource;
         this.element.appendChild(div);
       }
 
       if (!seenPseudoElement && rule.pseudoElement) {
         seenPseudoElement = true;
@@ -2673,17 +2227,17 @@ CssRuleView.prototype = {
  * @param {CssRuleView} aRuleView
  *        The CssRuleView containg the document holding this rule editor.
  * @param {Rule} aRule
  *        The Rule object we're editing.
  * @constructor
  */
 function RuleEditor(aRuleView, aRule) {
   this.ruleView = aRuleView;
-  this.doc = this.ruleView.doc;
+  this.doc = this.ruleView.styleDocument;
   this.rule = aRule;
 
   this.isEditable = !aRule.isSystem;
   // Flag that blocks updates of the selector and properties when it is
   // being edited
   this.isEditing = false;
 
   this._onNewProperty = this._onNewProperty.bind(this);
@@ -2724,17 +2278,17 @@ RuleEditor.prototype = {
       if (this.source.hasAttribute("unselectable")) {
         return;
       }
       let rule = this.rule.domRule;
       this.ruleView.emit("ruleview-linked-clicked", rule);
     }.bind(this));
     let sourceLabel = this.doc.createElementNS(XUL_NS, "label");
     sourceLabel.setAttribute("crop", "center");
-    sourceLabel.classList.add("source-link-label");
+    sourceLabel.classList.add("ruleview-rule-source-label");
     this.source.appendChild(sourceLabel);
 
     this.updateSourceLink();
 
     let code = createChild(this.element, "div", {
       class: "ruleview-code"
     });
 
@@ -2804,17 +2358,17 @@ RuleEditor.prototype = {
       // Create a property editor when the close brace is clicked.
       editableItem({ element: this.closeBrace }, () => {
         this.newProperty();
       });
     }
   },
 
   updateSourceLink: function() {
-    let sourceLabel = this.element.querySelector(".source-link-label");
+    let sourceLabel = this.element.querySelector(".ruleview-rule-source-label");
     let sourceHref = (this.rule.sheet && this.rule.sheet.href) ?
       this.rule.sheet.href : this.rule.title;
     let sourceLine = this.rule.ruleLine > 0 ? ":" + this.rule.ruleLine : "";
 
     sourceLabel.setAttribute("tooltiptext", sourceHref + sourceLine);
 
     if (this.rule.isSystem) {
       let uaLabel = _strings.GetStringFromName("rule.userAgentStyles");
@@ -3899,42 +3453,16 @@ function createChild(aParent, aTag, aAtt
         elt.setAttribute(attr, aAttributes[attr]);
       }
     }
   }
   aParent.appendChild(elt);
   return elt;
 }
 
-function createMenuItem(aMenu, aAttributes) {
-  let item = aMenu.ownerDocument.createElementNS(XUL_NS, "menuitem");
-
-  item.setAttribute("label", _strings.GetStringFromName(aAttributes.label));
-
-  if (aAttributes.accesskey) {
-    item.setAttribute("accesskey",
-                      _strings.GetStringFromName(aAttributes.accesskey));
-  }
-
-  item.addEventListener("command", aAttributes.command);
-
-  if (aAttributes.type) {
-    item.setAttribute("type", aAttributes.type);
-  }
-
-  aMenu.appendChild(item);
-
-  return item;
-}
-
-function createMenuSeparator(aMenu) {
-  let separator = aMenu.ownerDocument.createElementNS(XUL_NS, "menuseparator");
-  aMenu.appendChild(separator);
-}
-
 function setTimeout() {
   let window = Services.appShell.hiddenDOMWindow;
   return window.setTimeout.apply(window, arguments);
 }
 
 function clearTimeout() {
   let window = Services.appShell.hiddenDOMWindow;
   return window.clearTimeout.apply(window, arguments);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/style-inspector-menu.js
@@ -0,0 +1,534 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+/* globals overlays, Services, clipboardHelper, _strings */
+
+"use strict";
+
+const {Cc, Ci, Cu} = require("chrome");
+const {PREF_ORIG_SOURCES} = require("devtools/styleeditor/utils");
+
+loader.lazyRequireGetter(this, "overlays", "devtools/styleinspector/style-inspector-overlays");
+loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
+loader.lazyServiceGetter(this, "clipboardHelper", "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
+loader.lazyGetter(this, "_strings", () => {
+  return Services.strings
+  .createBundle("chrome://global/locale/devtools/styleinspector.properties");
+});
+
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const PREF_ENABLE_MDN_DOCS_TOOLTIP = "devtools.inspector.mdnDocsTooltip.enabled";
+
+/**
+ * Style inspector context menu
+ * @param {RuleView|ComputedView} view RuleView or ComputedView instance controlling this menu
+ * @param {Object} options menu configuration
+ */
+function StyleInspectorMenu(view, options) {
+  this.view = view;
+  this.inspector = this.view.inspector;
+  this.styleDocument = this.view.styleDocument;
+  this.styleWindow = this.view.styleWindow;
+
+  this.isRuleView = options.isRuleView;
+
+  this._onAddNewRule = this._onAddNewRule.bind(this);
+  this._onCopy = this._onCopy.bind(this);
+  this._onCopyColor = this._onCopyColor.bind(this);
+  this._onCopyImageDataUrl = this._onCopyImageDataUrl.bind(this);
+  this._onCopyLocation = this._onCopyLocation.bind(this);
+  this._onCopyPropertyDeclaration = this._onCopyPropertyDeclaration.bind(this);
+  this._onCopyPropertyName = this._onCopyPropertyName.bind(this);
+  this._onCopyPropertyValue = this._onCopyPropertyValue.bind(this);
+  this._onCopyRule = this._onCopyRule.bind(this);
+  this._onCopySelector = this._onCopySelector.bind(this);
+  this._onCopyUrl = this._onCopyUrl.bind(this);
+  this._onSelectAll = this._onSelectAll.bind(this);
+  this._onShowMdnDocs = this._onShowMdnDocs.bind(this);
+  this._onToggleOrigSources = this._onToggleOrigSources.bind(this);
+  this._updateMenuItems = this._updateMenuItems.bind(this);
+
+  this._createContextMenu();
+}
+
+module.exports = StyleInspectorMenu;
+
+StyleInspectorMenu.prototype = {
+  /**
+   * Display the style inspector context menu
+   */
+  show: function(event) {
+    try {
+      // In the sidebar we do not have this.styleDocument.popupNode
+      // so we need to save the node ourselves.
+      this.styleDocument.popupNode = event.explicitOriginalTarget;
+      this.styleWindow.focus();
+      this._menupopup.openPopupAtScreen(event.screenX, event.screenY, true);
+    } catch(e) {
+      console.error(e);
+    }
+  },
+
+  _createContextMenu: function() {
+    this._menupopup = this.styleDocument.createElementNS(XUL_NS, "menupopup");
+    this._menupopup.addEventListener("popupshowing", this._updateMenuItems);
+    this._menupopup.id = "computed-view-context-menu";
+
+    let parentDocument = this.styleWindow.parent.document;
+    let popupset = parentDocument.documentElement.querySelector("popupset");
+    if (!popupset) {
+      popupset = parentDocument.createElementNS(XUL_NS, "popupset");
+      parentDocument.documentElement.appendChild(popupset);
+    }
+    popupset.appendChild(this._menupopup);
+
+    this._createContextMenuItems();
+  },
+  /**
+   * Create all context menu items
+   */
+  _createContextMenuItems: function() {
+    this.menuitemCopy = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.copy",
+      accesskey: "styleinspector.contextmenu.copy.accessKey",
+      command: this._onCopy
+    });
+
+    this.menuitemCopyLocation = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.copyLocation",
+      command: this._onCopyLocation
+    });
+
+    this.menuitemCopyRule = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.copyRule",
+      command: this._onCopyRule
+    });
+
+    this.menuitemCopyColor = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.copyColor",
+      accesskey: "styleinspector.contextmenu.copyColor.accessKey",
+      command: this._onCopyColor
+    });
+
+    this.menuitemCopyUrl = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.copyUrl",
+      accesskey: "styleinspector.contextmenu.copyUrl.accessKey",
+      command: this._onCopyUrl
+    });
+
+    this.menuitemCopyImageDataUrl = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.copyImageDataUrl",
+      accesskey: "styleinspector.contextmenu.copyImageDataUrl.accessKey",
+      command: this._onCopyImageDataUrl
+    });
+
+    this.menuitemCopyPropertyDeclaration = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.copyPropertyDeclaration",
+      command: this._onCopyPropertyDeclaration
+    });
+
+    this.menuitemCopyPropertyName = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.copyPropertyName",
+      command: this._onCopyPropertyName
+    });
+
+    this.menuitemCopyPropertyValue = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.copyPropertyValue",
+      command: this._onCopyPropertyValue
+    });
+
+    this.menuitemCopySelector = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.copySelector",
+      command: this._onCopySelector
+    });
+
+    this._createMenuSeparator();
+
+    // Select All
+    this.menuitemSelectAll = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.selectAll",
+      accesskey: "styleinspector.contextmenu.selectAll.accessKey",
+      command: this._onSelectAll
+    });
+
+    this._createMenuSeparator();
+
+    // Add new rule
+    this.menuitemAddRule = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.addNewRule",
+      accesskey: "styleinspector.contextmenu.addNewRule.accessKey",
+      command: this._onAddNewRule
+    });
+
+    // Show MDN Docs
+    this.menuitemShowMdnDocs = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.showMdnDocs",
+      accesskey: "styleinspector.contextmenu.showMdnDocs.accessKey",
+      command: this._onShowMdnDocs
+    });
+
+    // Show Original Sources
+    this.menuitemSources = this._createContextMenuItem({
+      label: "styleinspector.contextmenu.toggleOrigSources",
+      accesskey: "styleinspector.contextmenu.toggleOrigSources.accessKey",
+      command: this._onToggleOrigSources,
+      type: "checkbox"
+    });
+  },
+
+  /**
+   * Create a single context menu item based on the provided configuration
+   * Returns the created menu item element
+   */
+  _createContextMenuItem: function(attributes) {
+    let ownerDocument = this._menupopup.ownerDocument;
+    let item = ownerDocument.createElementNS(XUL_NS, "menuitem");
+
+    item.setAttribute("label", _strings.GetStringFromName(attributes.label));
+    if (attributes.accesskey) {
+      item.setAttribute("accesskey", _strings.GetStringFromName(attributes.accesskey));
+    }
+    item.addEventListener("command", attributes.command);
+
+    if (attributes.type) {
+      item.setAttribute("type", attributes.type);
+    }
+
+    this._menupopup.appendChild(item);
+    return item;
+  },
+
+  _createMenuSeparator: function() {
+    let ownerDocument = this._menupopup.ownerDocument;
+    let separator = ownerDocument.createElementNS(XUL_NS, "menuseparator");
+    this._menupopup.appendChild(separator);
+  },
+
+  /**
+   * Update the context menu. This means enabling or disabling menuitems as
+   * appropriate.
+   */
+  _updateMenuItems: function() {
+    this._updateCopyMenuItems();
+
+    let showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
+    this.menuitemSources.setAttribute("checked", showOrig);
+
+    let enableMdnDocsTooltip = Services.prefs.getBoolPref(PREF_ENABLE_MDN_DOCS_TOOLTIP);
+    this.menuitemShowMdnDocs.hidden = !(enableMdnDocsTooltip && this._isPropertyName());
+
+    this.menuitemAddRule.disabled = !(this.isRuleView && !this.inspector.selection.isAnonymousNode());
+  },
+
+  /**
+   * Display the necessary copy context menu items depending on the clicked
+   * node and selection in the rule view.
+   */
+  _updateCopyMenuItems: function() {
+    this.menuitemCopy.hidden = !this._hasTextSelected();
+    this.menuitemCopyColor.hidden = !this._isColorPopup();
+    this.menuitemCopyImageDataUrl.hidden = !this._isImageUrl();
+
+    this.menuitemCopyRule.hidden = true;
+    this.menuitemCopyLocation.hidden = true;
+    this.menuitemCopyPropertyDeclaration.hidden = true;
+    this.menuitemCopyPropertyName.hidden = true;
+    this.menuitemCopyPropertyValue.hidden = true;
+    this.menuitemCopySelector.hidden = true;
+
+    this._clickedNodeInfo = this._getClickedNodeInfo();
+    if (this.isRuleView && this._clickedNodeInfo) {
+      this.menuitemCopyRule.hidden = false;
+
+      switch (this._clickedNodeInfo.type) {
+        case overlays.VIEW_NODE_PROPERTY_TYPE :
+          this.menuitemCopyPropertyDeclaration.hidden = false;
+          this.menuitemCopyPropertyName.hidden = false;
+          break;
+        case overlays.VIEW_NODE_VALUE_TYPE :
+          this.menuitemCopyPropertyDeclaration.hidden = false;
+          this.menuitemCopyPropertyValue.hidden = false;
+          break;
+        case overlays.VIEW_NODE_SELECTOR_TYPE :
+          this.menuitemCopySelector.hidden = false;
+          break;
+        case overlays.VIEW_NODE_LOCATION_TYPE :
+          this.menuitemCopyLocation.hidden = false;
+          break;
+      }
+    }
+  },
+
+  _hasTextSelected: function() {
+    let hasTextSelected;
+    let selection = this.styleWindow.getSelection();
+
+    let node = this._getClickedNode();
+    if (node.nodeName == "input") {
+       // input type="text"
+      let { selectionStart, selectionEnd } = node;
+      hasTextSelected = isFinite(selectionStart) && isFinite(selectionEnd)
+        && selectionStart !== selectionEnd;
+    } else {
+      hasTextSelected = selection.toString() && !selection.isCollapsed;
+    }
+
+    return hasTextSelected;
+  },
+
+  /**
+   * Get the type of the currently clicked node
+   */
+  _getClickedNodeInfo: function() {
+    let node = this._getClickedNode();
+    return this.view.getNodeInfo(node);
+  },
+
+  /**
+   * A helper that determines if the popup was opened with a click to a color
+   * value and saves the color to this._colorToCopy.
+   *
+   * @return {Boolean}
+   *         true if click on color opened the popup, false otherwise.
+   */
+  _isColorPopup: function() {
+    this._colorToCopy = "";
+
+    let container = this._getClickedNode();
+    if (!container) {
+      return false;
+    }
+
+    let isColorNode = el => el.dataset && "color" in el.dataset;
+
+    while (!isColorNode(container)) {
+      container = container.parentNode;
+      if (!container) {
+        return false;
+      }
+    }
+
+    this._colorToCopy = container.dataset.color;
+    return true;
+  },
+
+  _isPropertyName: function() {
+    let nodeInfo = this._getClickedNodeInfo();
+    if (!nodeInfo) {
+      return false;
+    }
+    return nodeInfo.type == overlays.VIEW_NODE_PROPERTY_TYPE;
+  },
+
+  /**
+   * Check if the current node (clicked node) is an image URL
+   * @return {Boolean} true if the node is an image url
+   */
+  _isImageUrl: function() {
+    let nodeInfo = this._getClickedNodeInfo();
+    if (!nodeInfo) {
+      return false;
+    }
+    return nodeInfo.type == overlays.VIEW_NODE_IMAGE_URL_TYPE;
+  },
+
+  /**
+   * Get the DOM Node container for the current popupNode.
+   * If popupNode is a textNode, return the parent node, otherwise return popupNode itself.
+   * @return {DOMNode}
+   */
+  _getClickedNode: function() {
+    let container = null;
+    let node = this.styleDocument.popupNode;
+
+    if (node) {
+      let isTextNode = node.nodeType == node.TEXT_NODE;
+      container = isTextNode ? node.parentElement : node;
+    }
+
+    return container;
+  },
+
+  /**
+   * Select all text.
+   */
+  _onSelectAll: function() {
+    let selection = this.styleWindow.getSelection();
+    selection.selectAllChildren(this.styleDocument.documentElement);
+  },
+
+  /**
+   * Copy the most recently selected color value to clipboard.
+   */
+  _onCopy: function(event) {
+    this.view.copySelection(this.styleDocument.popupNode);
+  },
+
+  /**
+   * Copy the most recently selected color value to clipboard.
+   */
+  _onCopyColor: function() {
+    clipboardHelper.copyString(this._colorToCopy);
+  },
+
+  /*
+   * Retrieve the url for the selected image and copy it to the clipboard
+   */
+  _onCopyUrl: function() {
+    clipboardHelper.copyString(this._clickedNodeInfo.value.url);
+  },
+
+  /**
+   * Retrieve the image data for the selected image url and copy it to the clipboard
+   */
+  _onCopyImageDataUrl: Task.async(function*() {
+    if (!this._clickedNodeInfo) {
+      return;
+    }
+
+    let message;
+    try {
+      let inspectorFront = this.inspector.inspector;
+      let imageUrl = this._clickedNodeInfo.value.url;
+      let data = yield inspectorFront.getImageDataFromURL(imageUrl);
+      message = yield data.data.string();
+    } catch (e) {
+      message = _strings.GetStringFromName("styleinspector.copyImageDataUrlError");
+    }
+
+    clipboardHelper.copyString(message);
+  }),
+
+  /**
+   *  Show docs from MDN for a CSS property.
+   */
+  _onShowMdnDocs: function() {
+    let cssPropertyName = this.styleDocument.popupNode.textContent;
+    let anchor = this.styleDocument.popupNode.parentNode;
+    let cssDocsTooltip = this.view.tooltips.cssDocs;
+    cssDocsTooltip.show(anchor, cssPropertyName);
+  },
+
+  /**
+   * Add a new rule to the current element.
+   */
+  _onAddNewRule: function() {
+    this.view._onAddRule();
+  },
+
+  /**
+   * Copy the rule source location of the current clicked node.
+   */
+  _onCopyLocation: function() {
+    if (!this._clickedNodeInfo) {
+      return;
+    }
+
+    clipboardHelper.copyString(this._clickedNodeInfo.value);
+  },
+
+  /**
+   * Copy the rule property declaration of the current clicked node.
+   */
+  _onCopyPropertyDeclaration: function() {
+    if (!this._clickedNodeInfo) {
+      return;
+    }
+
+    let textProp = this._clickedNodeInfo.value.textProperty;
+    clipboardHelper.copyString(textProp.stringifyProperty());
+  },
+
+  /**
+   * Copy the rule property name of the current clicked node.
+   */
+  _onCopyPropertyName: function() {
+    if (!this._clickedNodeInfo) {
+      return;
+    }
+
+    clipboardHelper.copyString(this._clickedNodeInfo.value.property);
+  },
+
+  /**
+   * Copy the rule property value of the current clicked node.
+   */
+  _onCopyPropertyValue: function() {
+    if (!this._clickedNodeInfo) {
+      return;
+    }
+
+    clipboardHelper.copyString(this._clickedNodeInfo.value.value);
+  },
+
+  /**
+   * Copy the rule of the current clicked node.
+   */
+  _onCopyRule: function() {
+    let ruleEditor = this.styleDocument.popupNode.parentNode.offsetParent._ruleEditor;
+    let rule = ruleEditor.rule;
+    clipboardHelper.copyString(rule.stringifyRule());
+  },
+
+  /**
+   * Copy the rule selector of the current clicked node.
+   */
+  _onCopySelector: function() {
+    if (!this._clickedNodeInfo) {
+      return;
+    }
+
+    clipboardHelper.copyString(this._clickedNodeInfo.value);
+  },
+
+  /**
+   *  Toggle the original sources pref.
+   */
+  _onToggleOrigSources: function() {
+    let isEnabled = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
+    Services.prefs.setBoolPref(PREF_ORIG_SOURCES, !isEnabled);
+  },
+
+  destroy: function() {
+    this._removeContextMenuItems();
+
+    // Destroy the context menu.
+    this._menupopup.removeEventListener("popupshowing", this._updateMenuItems);
+    this._menupopup.parentNode.removeChild(this._menupopup);
+    this._menupopup = null;
+
+    this.popupNode = null;
+    this.styleDocument.popupNode = null;
+    this.view = null;
+    this.inspector = null;
+    this.styleDocument = null;
+    this.styleWindow = null;
+  },
+
+  _removeContextMenuItems: function() {
+    this._removeContextMenuItem("menuitemAddRule", this._onAddNewRule);
+    this._removeContextMenuItem("menuitemCopy", this._onCopy);
+    this._removeContextMenuItem("menuitemCopyColor", this._onCopyColor);
+    this._removeContextMenuItem("menuitemCopyImageDataUrl", this._onCopyImageDataUrl);
+    this._removeContextMenuItem("menuitemCopyLocation", this._onCopyLocation);
+    this._removeContextMenuItem("menuitemCopyPropertyDeclaration", this._onCopyPropertyDeclaration);
+    this._removeContextMenuItem("menuitemCopyPropertyName", this._onCopyPropertyName);
+    this._removeContextMenuItem("menuitemCopyPropertyValue", this._onCopyPropertyValue);
+    this._removeContextMenuItem("menuitemCopyRule", this._onCopyRule);
+    this._removeContextMenuItem("menuitemCopySelector", this._onCopySelector);
+    this._removeContextMenuItem("menuitemCopyUrl", this._onCopyUrl);
+    this._removeContextMenuItem("menuitemSelectAll", this._onSelectAll);
+    this._removeContextMenuItem("menuitemShowMdnDocs", this._onShowMdnDocs);
+    this._removeContextMenuItem("menuitemSources", this._onToggleOrigSources);
+  },
+
+  _removeContextMenuItem: function(itemName, callback) {
+    if (this[itemName]) {
+      this[itemName].removeEventListener("command", callback);
+      this[itemName] = null;
+    }
+  }
+};
--- a/browser/devtools/styleinspector/style-inspector-overlays.js
+++ b/browser/devtools/styleinspector/style-inspector-overlays.js
@@ -36,17 +36,17 @@ const TOOLTIP_FONTFAMILY_TYPE = "font-fa
 const VIEW_NODE_SELECTOR_TYPE = exports.VIEW_NODE_SELECTOR_TYPE = 1;
 const VIEW_NODE_PROPERTY_TYPE = exports.VIEW_NODE_PROPERTY_TYPE = 2;
 const VIEW_NODE_VALUE_TYPE = exports.VIEW_NODE_VALUE_TYPE = 3;
 const VIEW_NODE_IMAGE_URL_TYPE = exports.VIEW_NODE_IMAGE_URL_TYPE = 4;
 const VIEW_NODE_LOCATION_TYPE = exports.VIEW_NODE_LOCATION_TYPE = 5;
 
 /**
  * Manages all highlighters in the style-inspector.
- * @param {CssRuleView|CssHtmlTree} view Either the rule-view or computed-view
+ * @param {CssRuleView|CssComputedView} view Either the rule-view or computed-view
  * panel
  */
 function HighlightersOverlay(view) {
   this.view = view;
 
   let {CssRuleView} = require("devtools/styleinspector/rule-view");
   this.isRuleView = view instanceof CssRuleView;
 
@@ -226,17 +226,17 @@ HighlightersOverlay.prototype = {
     this.highlighterUtils = null;
 
     this._isDestroyed = true;
   }
 };
 
 /**
  * Manages all tooltips in the style-inspector.
- * @param {CssRuleView|CssHtmlTree} view Either the rule-view or computed-view
+ * @param {CssRuleView|CssComputedView} view Either the rule-view or computed-view
  * panel
  */
 function TooltipsOverlay(view) {
   this.view = view;
 
   let {CssRuleView} = require("devtools/styleinspector/rule-view");
   this.isRuleView = view instanceof CssRuleView;
 
@@ -263,23 +263,24 @@ TooltipsOverlay.prototype = {
       return;
     }
 
     // Image, fonts, ... preview tooltip
     this.previewTooltip = new Tooltip(this.view.inspector.panelDoc);
     this.previewTooltip.startTogglingOnHover(this.view.element,
       this._onPreviewTooltipTargetHover.bind(this));
 
+    // MDN CSS help tooltip
+    this.cssDocs = new CssDocsTooltip(this.view.inspector.panelDoc);
+
     if (this.isRuleView) {
       // Color picker tooltip
       this.colorPicker = new SwatchColorPickerTooltip(this.view.inspector.panelDoc);
       // Cubic bezier tooltip
       this.cubicBezier = new SwatchCubicBezierTooltip(this.view.inspector.panelDoc);
-      // MDN CSS help tooltip
-      this.cssDocs = new CssDocsTooltip(this.view.inspector.panelDoc);
       // Filter editor tooltip
       this.filterEditor = new SwatchFilterTooltip(this.view.inspector.panelDoc);
     }
 
     this._isStarted = true;
   },
 
   /**
--- a/browser/devtools/styleinspector/style-inspector.js
+++ b/browser/devtools/styleinspector/style-inspector.js
@@ -16,19 +16,19 @@ loader.lazyGetter(this, "ComputedView", 
 loader.lazyGetter(this, "_strings", () => Services.strings
   .createBundle("chrome://global/locale/devtools/styleinspector.properties"));
 
 // This module doesn't currently export any symbols directly, it only
 // registers inspector tools.
 
 function RuleViewTool(inspector, window, iframe) {
   this.inspector = inspector;
-  this.doc = window.document;
+  this.document = window.document;
 
-  this.view = new RuleView.CssRuleView(inspector, this.doc);
+  this.view = new RuleView.CssRuleView(this.inspector, this.document);
 
   this.onLinkClicked = this.onLinkClicked.bind(this);
   this.onSelected = this.onSelected.bind(this);
   this.refresh = this.refresh.bind(this);
   this.clearUserProperties = this.clearUserProperties.bind(this);
   this.onPropertyChanged = this.onPropertyChanged.bind(this);
   this.onViewRefreshed = this.onViewRefreshed.bind(this);
   this.onPanelSelected = this.onPanelSelected.bind(this);
@@ -148,25 +148,25 @@ RuleViewTool.prototype = {
     this.inspector.sidebar.off("ruleview-selected", this.onPanelSelected);
 
     this.view.off("ruleview-linked-clicked", this.onLinkClicked);
     this.view.off("ruleview-changed", this.onPropertyChanged);
     this.view.off("ruleview-refreshed", this.onViewRefreshed);
 
     this.view.destroy();
 
-    this.view = this.doc = this.inspector = null;
+    this.view = this.document = this.inspector = null;
   }
 };
 
 function ComputedViewTool(inspector, window, iframe) {
   this.inspector = inspector;
-  this.doc = window.document;
+  this.document = window.document;
 
-  this.view = new ComputedView.CssHtmlTree(this, inspector.pageStyle);
+  this.view = new ComputedView.CssComputedView(this.inspector, this.document, this.inspector.pageStyle);
 
   this.onSelected = this.onSelected.bind(this);
   this.refresh = this.refresh.bind(this);
   this.onPanelSelected = this.onPanelSelected.bind(this);
 
   this.inspector.selection.on("detached", this.onSelected);
   this.inspector.selection.on("new-node-front", this.onSelected);
   this.inspector.on("layout-change", this.refresh);
@@ -233,15 +233,14 @@ ComputedViewTool.prototype = {
     this.inspector.off("layout-change", this.refresh);
     this.inspector.sidebar.off("computedview-selected", this.refresh);
     this.inspector.selection.off("pseudoclass", this.refresh);
     this.inspector.selection.off("new-node-front", this.onSelected);
     this.inspector.sidebar.off("computedview-selected", this.onPanelSelected);
 
     this.view.destroy();
 
-    this.view = this.cssLogic = this.cssHtmlTree = null;
-    this.doc = this.inspector = null;
+    this.view = this.document = this.inspector = null;
   }
 };
 
 exports.RuleViewTool = RuleViewTool;
 exports.ComputedViewTool = ComputedViewTool;
--- a/browser/devtools/styleinspector/test/browser_computedview_select-and-copy-styles.js
+++ b/browser/devtools/styleinspector/test/browser_computedview_select-and-copy-styles.js
@@ -70,18 +70,18 @@ function checkCopySelection(view) {
 }
 
 function checkSelectAll(view) {
   info("Testing select-all copy");
 
   let contentDoc = view.styleDocument;
   let prop = contentDoc.querySelector(".property-view");
 
-  info("Checking that _SelectAll() then copy returns the correct clipboard value");
-  view._onSelectAll();
+  info("Checking that _onSelectAll() then copy returns the correct clipboard value");
+  view._contextmenu._onSelectAll();
   let expectedPattern = "color: #FF0;[\\r\\n]+" +
                         "font-family: helvetica,sans-serif;[\\r\\n]+" +
                         "font-size: 16px;[\\r\\n]+" +
                         "font-variant-caps: small-caps;[\\r\\n]*";
 
   return waitForClipboard(() => {
     fireCopyEvent(prop);
   }, () => {
--- a/browser/devtools/styleinspector/test/browser_ruleview_add-property-cancel_02.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_add-property-cancel_02.js
@@ -38,39 +38,39 @@ add_task(function*() {
   is(inplaceEditor(elementRuleEditor.newPropSpan), editor, "The new property editor got focused.");
 
   info("Entering a value in the property name editor");
   editor.input.value = "color";
 
   info("Pressing return to commit and focus the new value field");
   let onValueFocus = once(elementRuleEditor.element, "focus", true);
   let onRuleViewChanged = view.once("ruleview-changed");
-  EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
   yield onValueFocus;
   yield onRuleViewChanged;
 
   // Getting the new value editor after focus
-  editor = inplaceEditor(view.doc.activeElement);
+  editor = inplaceEditor(view.styleDocument.activeElement);
   let textProp = elementRuleEditor.rule.textProps[1];
 
   is(elementRuleEditor.rule.textProps.length,  2, "Created a new text property.");
   is(elementRuleEditor.propertyList.children.length, 2, "Created a property editor.");
   is(editor, inplaceEditor(textProp.editor.valueSpan), "Editing the value span now.");
 
   info("Entering a property value");
   editor.input.value = "red";
 
   info("Escaping out of the field");
   onRuleViewChanged = view.once("ruleview-changed");
-  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
   yield onRuleViewChanged;
 
   info("Checking that the previous field is focused");
   let focusedElement = inplaceEditor(elementRuleEditor.rule.textProps[0].editor.valueSpan).input;
   is(focusedElement, focusedElement.ownerDocument.activeElement, "Correct element has focus");
 
   onRuleViewChanged = view.once("ruleview-changed");
-  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
   yield onRuleViewChanged;
 
   is(elementRuleEditor.rule.textProps.length,  1, "Removed the new text property.");
   is(elementRuleEditor.propertyList.children.length, 1, "Removed the property editor.");
 });
--- a/browser/devtools/styleinspector/test/browser_ruleview_add-property-cancel_03.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_add-property-cancel_03.js
@@ -45,17 +45,17 @@ function* testCancelNewOnEscape(inspecto
   // Start at the beginning: start to add a rule to the element's style
   // declaration, add some text, then press escape.
 
   let elementRuleEditor = getRuleViewRuleEditor(ruleView, 0);
   let editor = yield focusEditableField(ruleView, elementRuleEditor.closeBrace);
 
   is(inplaceEditor(elementRuleEditor.newPropSpan), editor, "Next focused editor should be the new property editor.");
 
-  EventUtils.sendString("background", ruleView.doc.defaultView)
+  EventUtils.sendString("background", ruleView.styleWindow)
 
   let onBlur = once(editor.input, "blur");
   EventUtils.synthesizeKey("VK_ESCAPE", {});
   yield onBlur;
 
   ok(!elementRuleEditor.rule._applyingModifications, "Shouldn't have an outstanding modification request after a cancel.");
   is(elementRuleEditor.rule.textProps.length, 0, "Should have canceled creating a new text property.");
   ok(!elementRuleEditor.propertyList.hasChildNodes(), "Should not have any properties.");
--- a/browser/devtools/styleinspector/test/browser_ruleview_add-property-svg.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_add-property-svg.js
@@ -34,29 +34,29 @@ function* testCreateNew(ruleView) {
 
   let input = editor.input;
 
   info("Entering the property name");
   input.value = "fill";
 
   info("Pressing RETURN and waiting for the value field focus");
   let onFocus = once(elementRuleEditor.element, "focus", true);
-  EventUtils.sendKey("return", ruleView.doc.defaultView);
+  EventUtils.sendKey("return", ruleView.styleWindow);
   yield onFocus;
   yield elementRuleEditor.rule._applyingModifications;
 
-  editor = inplaceEditor(ruleView.doc.activeElement);
+  editor = inplaceEditor(ruleView.styleDocument.activeElement);
 
   is(elementRuleEditor.rule.textProps.length,  1, "Should have created a new text property.");
   is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
   let textProp = elementRuleEditor.rule.textProps[0];
   is(editor, inplaceEditor(textProp.editor.valueSpan), "Should be editing the value span now.");
 
   editor.input.value = "red";
   let onBlur = once(editor.input, "blur");
-  EventUtils.sendKey("return", ruleView.doc.defaultView);
+  EventUtils.sendKey("return", ruleView.styleWindow);
   yield onBlur;
   yield elementRuleEditor.rule._applyingModifications;
 
   is(textProp.value, "red", "Text prop should have been changed.");
 
   is((yield getComputedStyleProperty(TEST_SELECTOR, null, "fill")), "rgb(255, 0, 0)", "The fill was changed to red");
 }
--- a/browser/devtools/styleinspector/test/browser_ruleview_add-property_01.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_add-property_01.js
@@ -48,22 +48,22 @@ function* testCreateNew(view) {
   let input = editor.input;
 
   info("Entering background-color in the property name editor");
   input.value = "background-color";
 
   info("Pressing return to commit and focus the new value field");
   let onValueFocus = once(elementRuleEditor.element, "focus", true);
   let onModifications = elementRuleEditor.rule._applyingModifications;
-  EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
   yield onValueFocus;
   yield onModifications;
 
   // Getting the new value editor after focus
-  editor = inplaceEditor(view.doc.activeElement);
+  editor = inplaceEditor(view.styleDocument.activeElement);
   let textProp = elementRuleEditor.rule.textProps[0];
 
   is(elementRuleEditor.rule.textProps.length,  1, "Created a new text property.");
   is(elementRuleEditor.propertyList.children.length, 1, "Created a property editor.");
   is(editor, inplaceEditor(textProp.editor.valueSpan), "Editing the value span now.");
 
   info("Entering a value and bluring the field to expect a rule change");
   editor.input.value = "#XYZ";
--- a/browser/devtools/styleinspector/test/browser_ruleview_add-property_02.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_add-property_02.js
@@ -35,36 +35,36 @@ function* testCreateNew(inspector, ruleV
     "Next focused editor should be the new property editor.");
 
   let input = editor.input;
 
   ok(input.selectionStart === 0 && input.selectionEnd === input.value.length,
     "Editor contents are selected.");
 
   // Try clicking on the editor's input again, shouldn't cause trouble (see bug 761665).
-  EventUtils.synthesizeMouse(input, 1, 1, {}, ruleView.doc.defaultView);
+  EventUtils.synthesizeMouse(input, 1, 1, {}, ruleView.styleWindow);
   input.select();
 
   info("Entering the property name");
   input.value = "background-color";
 
   info("Pressing RETURN and waiting for the value field focus");
   let onFocus = once(elementRuleEditor.element, "focus", true);
-  EventUtils.sendKey("return", ruleView.doc.defaultView);
+  EventUtils.sendKey("return", ruleView.styleWindow);
   yield onFocus;
   yield elementRuleEditor.rule._applyingModifications;
 
-  editor = inplaceEditor(ruleView.doc.activeElement);
+  editor = inplaceEditor(ruleView.styleDocument.activeElement);
 
   is(elementRuleEditor.rule.textProps.length,  1, "Should have created a new text property.");
   is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
   let textProp = elementRuleEditor.rule.textProps[0];
   is(editor, inplaceEditor(textProp.editor.valueSpan), "Should be editing the value span now.");
 
   let onMutated = inspector.once("markupmutation");
   editor.input.value = "purple";
   let onBlur = once(editor.input, "blur");
-  EventUtils.sendKey("return", ruleView.doc.defaultView);
+  EventUtils.sendKey("return", ruleView.styleWindow);
   yield onBlur;
   yield onMutated;
 
   is(textProp.value, "purple", "Text prop should have been changed.");
 }
--- a/browser/devtools/styleinspector/test/browser_ruleview_add-rule_01.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_add-rule_01.js
@@ -56,28 +56,28 @@ function* runTestData(inspector, view, d
 
   info("Resetting page content");
   content.document.body.innerHTML = PAGE_CONTENT;
 }
 
 function* addNewRule(inspector, view, method) {
   if (method == "context-menu") {
     info("Waiting for context menu to be shown");
-    let onPopup = once(view._contextmenu, "popupshown");
-    let win = view.doc.defaultView;
+    let onPopup = once(view._contextmenu._menupopup, "popupshown");
+    let win = view.styleWindow;
 
     EventUtils.synthesizeMouseAtCenter(view.element,
       {button: 2, type: "contextmenu"}, win);
     yield onPopup;
 
-    ok(!view.menuitemAddRule.hidden, "Add rule is visible");
+    ok(!view._contextmenu.menuitemAddRule.hidden, "Add rule is visible");
 
     info("Adding the new rule");
-    view.menuitemAddRule.click();
-    view._contextmenu.hidePopup();
+    view._contextmenu.menuitemAddRule.click();
+    view._contextmenu._menupopup.hidePopup();
   }
   else {
     info("Adding the new rule using the button");
     view.addRuleButton.click();
   }
   info("Waiting for rule view to change");
   yield view.once("ruleview-changed");
 }
--- a/browser/devtools/styleinspector/test/browser_ruleview_completion-existing-property_01.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_completion-existing-property_01.js
@@ -60,17 +60,17 @@ let TEST_URL = "data:text/html;charset=u
 add_task(function*() {
   yield addTab(TEST_URL);
   let {toolbox, inspector, view} = yield openRuleView();
 
   info("Selecting the test node");
   yield selectNode("h1", inspector);
 
   info("Focusing the css property editable field");
-  let propertyName = view.doc.querySelectorAll(".ruleview-propertyname")[0];
+  let propertyName = view.styleDocument.querySelectorAll(".ruleview-propertyname")[0];
   let editor = yield focusEditableField(view, propertyName);
 
   info("Starting to test for css property completion");
   for (let i = 0; i < testData.length; i ++) {
     yield testCompletion(testData[i], editor, view);
   }
 });
 
@@ -84,17 +84,17 @@ function* testCompletion([key, completio
     info("Adding event listener for left|right|back_space|escape|home|end|page_up|page_down keys");
     onSuggest = once(editor.input, "keypress");
   } else {
     info("Waiting for after-suggest event on the editor");
     onSuggest = editor.once("after-suggest");
   }
 
   info("Synthesizing key " + key);
-  EventUtils.synthesizeKey(key, {}, view.doc.defaultView);
+  EventUtils.synthesizeKey(key, {}, view.styleWindow);
 
   yield onSuggest;
   yield wait(1); // Equivalent of executeSoon
 
   info("Checking the state");
   if (completion != null) {
     is(editor.input.value, completion, "Correct value is autocompleted");
   }
--- a/browser/devtools/styleinspector/test/browser_ruleview_completion-existing-property_02.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_completion-existing-property_02.js
@@ -42,55 +42,55 @@ let TEST_URL = "data:text/html;charset=u
 add_task(function*() {
   yield addTab(TEST_URL);
   let {toolbox, inspector, view} = yield openRuleView();
 
   info("Selecting the test node");
   yield selectNode("h1", inspector);
 
   info("Focusing the css property editable value");
-  let value = view.doc.querySelectorAll(".ruleview-propertyvalue")[0];
+  let value = view.styleDocument.querySelectorAll(".ruleview-propertyvalue")[0];
   let editor = yield focusEditableField(view, value);
 
   info("Starting to test for css property completion");
   for (let i = 0; i < testData.length; i ++) {
     // Re-define the editor at each iteration, because the focus may have moved
     // from property to value and back
-    editor = inplaceEditor(view.doc.activeElement);
+    editor = inplaceEditor(view.styleDocument.activeElement);
     yield testCompletion(testData[i], editor, view);
   }
 });
 
 function* testCompletion([key, modifiers, completion, index, total], editor, view) {
   info("Pressing key " + key);
   info("Expecting " + completion + ", " + index + ", " + total);
 
   let onKeyPress;
 
   if (/tab/ig.test(key)) {
     info("Waiting for the new property or value editor to get focused");
-    let brace = view.doc.querySelector(".ruleview-ruleclose");
+    let brace = view.styleDocument.querySelector(".ruleview-ruleclose");
     onKeyPress = once(brace.parentNode, "focus", true);
   } else if (/(right|return|back_space)/ig.test(key)) {
     info("Adding event listener for right|return|back_space keys");
     onKeyPress = once(editor.input, "keypress");
   } else {
     info("Waiting for after-suggest event on the editor");
     onKeyPress = editor.once("after-suggest");
   }
 
   info("Synthesizing key " + key + ", modifiers: " + Object.keys(modifiers));
-  EventUtils.synthesizeKey(key, modifiers, view.doc.defaultView);
+  EventUtils.synthesizeKey(key, modifiers, view.styleWindow);
 
   yield onKeyPress;
   yield wait(1); // Equivalent of executeSoon
 
   // The key might have been a TAB or shift-TAB, in which case the editor will
   // be a new one
-  editor = inplaceEditor(view.doc.activeElement);
+  editor = inplaceEditor(view.styleDocument.activeElement);
 
   info("Checking the state");
   if (completion != null) {
     is(editor.input.value, completion, "Correct value is autocompleted");
   }
   if (total == 0) {
     ok(!(editor.popup && editor.popup.isOpen), "Popup is closed");
   } else {
--- a/browser/devtools/styleinspector/test/browser_ruleview_completion-new-property_01.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_completion-new-property_01.js
@@ -43,17 +43,17 @@ let TEST_URL = "data:text/html;charset=u
 add_task(function*() {
   yield addTab(TEST_URL);
   let {toolbox, inspector, view} = yield openRuleView();
 
   info("Selecting the test node");
   yield selectNode("h1", inspector);
 
   info("Focusing the css property editable field");
-  let brace = view.doc.querySelector(".ruleview-ruleclose");
+  let brace = view.styleDocument.querySelector(".ruleview-ruleclose");
   let editor = yield focusEditableField(view, brace);
 
   info("Starting to test for css property completion");
   for (let i = 0; i < testData.length; i ++) {
     yield testCompletion(testData[i], editor, view);
   }
 });
 
@@ -67,17 +67,17 @@ function* testCompletion([key, completio
     info("Adding event listener for right|back_space|escape keys");
     onSuggest = once(editor.input, "keypress");
   } else {
     info("Waiting for after-suggest event on the editor");
     onSuggest = editor.once("after-suggest");
   }
 
   info("Synthesizing key " + key);
-  EventUtils.synthesizeKey(key, {}, view.doc.defaultView);
+  EventUtils.synthesizeKey(key, {}, view.styleWindow);
 
   yield onSuggest;
   yield wait(1); // Equivalent of executeSoon
 
   info("Checking the state");
   if (completion != null) {
     is(editor.input.value, completion, "Correct value is autocompleted");
   }
--- a/browser/devtools/styleinspector/test/browser_ruleview_completion-new-property_02.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_completion-new-property_02.js
@@ -45,58 +45,58 @@ let TEST_URL = "data:text/html;charset=u
 add_task(function*() {
   yield addTab(TEST_URL);
   let {toolbox, inspector, view} = yield openRuleView();
 
   info("Selecting the test node");
   yield selectNode("h1", inspector);
 
   info("Focusing a new css property editable property");
-  let brace = view.doc.querySelectorAll(".ruleview-ruleclose")[1];
+  let brace = view.styleDocument.querySelectorAll(".ruleview-ruleclose")[1];
   let editor = yield focusEditableField(view, brace);
 
   info("Starting to test for css property completion");
   for (let i = 0; i < testData.length; i ++) {
     // Re-define the editor at each iteration, because the focus may have moved
     // from property to value and back
-    editor = inplaceEditor(view.doc.activeElement);
+    editor = inplaceEditor(view.styleDocument.activeElement);
     yield testCompletion(testData[i], editor, view);
   }
 });
 
 function* testCompletion([key, modifiers, completion, index, total], editor, view) {
   info("Pressing key " + key);
   info("Expecting " + completion + ", " + index + ", " + total);
 
   let onKeyPress;
 
   if (/tab/ig.test(key)) {
     info("Waiting for the new property or value editor to get focused");
-    let brace = view.doc.querySelectorAll(".ruleview-ruleclose")[1];
+    let brace = view.styleDocument.querySelectorAll(".ruleview-ruleclose")[1];
     onKeyPress = once(brace.parentNode, "focus", true);
   } else if (/(right|back_space|escape|return)/ig.test(key) ||
              (modifiers.accelKey || modifiers.ctrlKey)) {
     info("Adding event listener for right|escape|back_space|return keys");
     onKeyPress = once(editor.input, "keypress");
   } else {
     info("Waiting for after-suggest event on the editor");
     onKeyPress = editor.once("after-suggest");
   }
 
   info("Synthesizing key " + key + ", modifiers: " + Object.keys(modifiers));
-  EventUtils.synthesizeKey(key, modifiers, view.doc.defaultView);
+  EventUtils.synthesizeKey(key, modifiers, view.styleWindow);
 
   yield onKeyPress;
   yield wait(1); // Equivalent of executeSoon
 
   info("Checking the state");
   if (completion != null) {
     // The key might have been a TAB or shift-TAB, in which case the editor will
     // be a new one
-    editor = inplaceEditor(view.doc.activeElement);
+    editor = inplaceEditor(view.styleDocument.activeElement);
     is(editor.input.value, completion, "Correct value is autocompleted");
   }
   if (total == 0) {
     ok(!(editor.popup && editor.popup.isOpen), "Popup is closed");
   } else {
     ok(editor.popup._panel.state == "open" || editor.popup._panel.state == "showing", "Popup is open");
     is(editor.popup.getItems().length, total, "Number of suggestions match");
     is(editor.popup.selectedIndex, index, "Correct item is selected");
--- a/browser/devtools/styleinspector/test/browser_ruleview_content_02.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_content_02.js
@@ -44,33 +44,33 @@ add_task(function*() {
   let inspector = toolbox.getPanel("inspector");
   yield inspector.once("inspector-updated");
 
   let view = inspector.sidebar.getWindowForTab("ruleview")["ruleview"].view;
 
   checkRuleViewContent(view);
 });
 
-function checkRuleViewContent({doc}) {
+function checkRuleViewContent({styleDocument}) {
   info("Making sure the rule-view contains the expected content");
 
-  let headers = [...doc.querySelectorAll(".ruleview-header")];
+  let headers = [...styleDocument.querySelectorAll(".ruleview-header")];
   is(headers.length, 3, "There are 3 headers for inherited rules");
 
   is(headers[0].textContent,
     STRINGS.formatStringFromName("rule.inheritedFrom", ["p"], 1),
     "The first header is correct");
   is(headers[1].textContent,
     STRINGS.formatStringFromName("rule.inheritedFrom", ["div"], 1),
     "The second header is correct");
   is(headers[2].textContent,
     STRINGS.formatStringFromName("rule.inheritedFrom", ["body"], 1),
     "The third header is correct");
 
-  let rules = doc.querySelectorAll(".ruleview-rule");
+  let rules = styleDocument.querySelectorAll(".ruleview-rule");
   is(rules.length, 4, "There are 4 rules in the view");
 
   for (let rule of rules) {
     let selector = rule.querySelector(".ruleview-selectorcontainer");
     is(selector.textContent,
       STRINGS.GetStringFromName("rule.sourceElement"),
       "The rule's selector is correct");
 
--- a/browser/devtools/styleinspector/test/browser_ruleview_context-menu-show-mdn-docs-01.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_context-menu-show-mdn-docs-01.js
@@ -57,34 +57,33 @@ add_task(function* () {
  *   depending on popupNode
  */
 function* testMdnContextMenuItemVisibility(view) {
   info("Test that MDN context menu item is shown only when it should be.");
 
   let root = rootElement(view);
   for (let node of iterateNodes(root)) {
     info("Setting " + node + " as popupNode");
-    view.doc.popupNode = node;
+    view.styleDocument.popupNode = node;
 
     info("Updating context menu state");
-    view._contextMenuUpdate();
-    let isVisible = !view.menuitemShowMdnDocs.hidden;
+    view._contextmenu._updateMenuItems();
+    let isVisible = !view._contextmenu.menuitemShowMdnDocs.hidden;
     let shouldBeVisible = isPropertyNameNode(node);
     let message = shouldBeVisible? "shown": "hidden";
     is(isVisible, shouldBeVisible,
-       "The MDN context menu item is " + message);
+       "The MDN context menu item is " + message + " ; content : " + node.textContent + " ; type : " + node.nodeType);
   }
 }
 
 /**
  * Check if a node is a property name.
  */
 function isPropertyNameNode(node) {
-  return ((node.nodeType === node.TEXT_NODE) &&
-          (node.textContent === "font-family"));
+  return node.textContent === "font-family";
 }
 
 /**
  * A generator that iterates recursively through all child nodes of baseNode.
  */
 function* iterateNodes(baseNode) {
   yield baseNode;
 
--- a/browser/devtools/styleinspector/test/browser_ruleview_context-menu-show-mdn-docs-02.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_context-menu-show-mdn-docs-02.js
@@ -42,24 +42,24 @@ add_task(function* () {
 
 function* testShowMdnTooltip(view) {
   setBaseCssDocsUrl(TEST_URL_ROOT);
 
   info("Setting the popupNode for the MDN docs tooltip");
 
   let {nameSpan} = getRuleViewProperty(view, "element", PROPERTYNAME);
 
-  view.doc.popupNode = nameSpan.firstChild;
-  view._contextMenuUpdate();
+  view.styleDocument.popupNode = nameSpan.firstChild;
+  view._contextmenu._updateMenuItems();
 
   let cssDocs = view.tooltips.cssDocs;
 
   info("Showing the MDN docs tooltip");
   let onShown = cssDocs.tooltip.once("shown");
-  view.menuitemShowMdnDocs.click();
+  view._contextmenu.menuitemShowMdnDocs.click();
   yield onShown;
   ok(true, "The MDN docs tooltip was shown");
 }
 
 /**
  * Test that:
  *  - the MDN tooltip is shown when we click the context menu item
  *  - the tooltip's contents have been initialized (we don't fully
--- a/browser/devtools/styleinspector/test/browser_ruleview_context-menu-show-mdn-docs-03.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_context-menu-show-mdn-docs-03.js
@@ -96,21 +96,21 @@ function* setBooleanPref(pref, state) {
  */
 function* testMdnContextMenuItemVisibility(view, shouldBeVisible) {
   let message = shouldBeVisible? "shown": "hidden";
   info("Test that MDN context menu item is " + message);
 
   info("Set a CSS property name as popupNode");
   let root = rootElement(view);
   let node = root.querySelector("." + PROPERTY_NAME_CLASS).firstChild;
-  view.doc.popupNode = node;
+  view.styleDocument.popupNode = node;
 
   info("Update context menu state");
-  view._contextMenuUpdate();
-  let isVisible = !view.menuitemShowMdnDocs.hidden;
+  view._contextmenu._updateMenuItems();
+  let isVisible = !view._contextmenu.menuitemShowMdnDocs.hidden;
   is(isVisible, shouldBeVisible,
      "The MDN context menu item is " + message);
 }
 
 /**
  * Returns the root element for the rule view.
  */
 let rootElement = view => (view.element) ? view.element : view.styleDocument;
--- a/browser/devtools/styleinspector/test/browser_ruleview_copy_styles.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_copy_styles.js
@@ -13,68 +13,68 @@ XPCOMUtils.defineLazyGetter(this, "osStr
   return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
 });
 
 let TEST_URI = TEST_URL_ROOT + "doc_copystyles.html";
 
 add_task(function*() {
   yield addTab(TEST_URI);
   let { inspector, view } = yield openRuleView();
-
+  let contextmenu = view._contextmenu;
   yield selectNode("#testid", inspector);
 
   let ruleEditor = getRuleViewRuleEditor(view, 1);
 
   let data = [
     {
       desc: "Test Copy Property Name",
       node: ruleEditor.rule.textProps[0].editor.nameSpan,
-      menuItem: view.menuitemCopyPropertyName,
+      menuItem: contextmenu.menuitemCopyPropertyName,
       expectedPattern: "color",
       hidden: {
         copyLocation: true,
         copyPropertyDeclaration: false,
         copyPropertyName: false,
         copyPropertyValue: true,
         copySelector: true,
         copyRule: false
       }
     },
     {
       desc: "Test Copy Property Value",
       node: ruleEditor.rule.textProps[2].editor.valueSpan,
-      menuItem: view.menuitemCopyPropertyValue,
+      menuItem: contextmenu.menuitemCopyPropertyValue,
       expectedPattern: "12px",
       hidden: {
         copyLocation: true,
         copyPropertyDeclaration: false,
         copyPropertyName: true,
         copyPropertyValue: false,
         copySelector: true,
         copyRule: false
       }
     },
     {
       desc: "Test Copy Property Declaration",
       node: ruleEditor.rule.textProps[2].editor.nameSpan,
-      menuItem: view.menuitemCopyPropertyDeclaration,
+      menuItem: contextmenu.menuitemCopyPropertyDeclaration,
       expectedPattern: "font-size: 12px;",
       hidden: {
         copyLocation: true,
         copyPropertyDeclaration: false,
         copyPropertyName: false,
         copyPropertyValue: true,
         copySelector: true,
         copyRule: false
       }
     },
     {
       desc: "Test Copy Rule",
       node: ruleEditor.rule.textProps[2].editor.nameSpan,
-      menuItem: view.menuitemCopyRule,
+      menuItem: contextmenu.menuitemCopyRule,
       expectedPattern: "#testid {[\\r\\n]+" +
                        "\tcolor: #F00;[\\r\\n]+" +
                        "\tbackground-color: #00F;[\\r\\n]+" +
                        "\tfont-size: 12px;[\\r\\n]+" +
                        "}",
       hidden: {
         copyLocation: true,
         copyPropertyDeclaration: false,
@@ -82,31 +82,31 @@ add_task(function*() {
         copyPropertyValue: true,
         copySelector: true,
         copyRule: false
       }
     },
     {
       desc: "Test Copy Selector",
       node: ruleEditor.selectorText,
-      menuItem: view.menuitemCopySelector,
+      menuItem: contextmenu.menuitemCopySelector,
       expectedPattern: "html, body, #testid",
       hidden: {
         copyLocation: true,
         copyPropertyDeclaration: true,
         copyPropertyName: true,
         copyPropertyValue: true,
         copySelector: false,
         copyRule: false
       }
     },
     {
       desc: "Test Copy Location",
       node: ruleEditor.source,
-      menuItem: view.menuitemCopyLocation,
+      menuItem: contextmenu.menuitemCopyLocation,
       expectedPattern: "http://example.com/browser/browser/devtools/" +
                        "styleinspector/test/doc_copystyles.css",
       hidden: {
         copyLocation: false,
         copyPropertyDeclaration: true,
         copyPropertyName: true,
         copyPropertyValue: true,
         copySelector: true,
@@ -114,17 +114,17 @@ add_task(function*() {
       }
     },
     {
       setup: function*() {
         yield disableProperty(view);
       },
       desc: "Test Copy Rule with Disabled Property",
       node: ruleEditor.rule.textProps[2].editor.nameSpan,
-      menuItem: view.menuitemCopyRule,
+      menuItem: contextmenu.menuitemCopyRule,
       expectedPattern: "#testid {[\\r\\n]+" +
                        "\t\/\\* color: #F00; \\*\/[\\r\\n]+" +
                        "\tbackground-color: #00F;[\\r\\n]+" +
                        "\tfont-size: 12px;[\\r\\n]+" +
                        "}",
       hidden: {
         copyLocation: true,
         copyPropertyDeclaration: false,
@@ -132,17 +132,17 @@ add_task(function*() {
         copyPropertyValue: true,
         copySelector: true,
         copyRule: false
       }
     },
     {
       desc: "Test Copy Property Declaration with Disabled Property",
       node: ruleEditor.rule.textProps[0].editor.nameSpan,
-      menuItem: view.menuitemCopyPropertyDeclaration,
+      menuItem: contextmenu.menuitemCopyPropertyDeclaration,
       expectedPattern: "\/\\* color: #F00; \\*\/",
       hidden: {
         copyLocation: true,
         copyPropertyDeclaration: false,
         copyPropertyName: false,
         copyPropertyValue: true,
         copySelector: true,
         copyRule: false
@@ -156,63 +156,61 @@ add_task(function*() {
     }
 
     info(desc);
     yield checkCopyStyle(view, node, menuItem, expectedPattern, hidden);
   }
 });
 
 function* checkCopyStyle(view, node, menuItem, expectedPattern, hidden) {
-  let win = view.doc.defaultView;
-
-  let onPopup = once(view._contextmenu, "popupshown");
+  let onPopup = once(view._contextmenu._menupopup, "popupshown");
   EventUtils.synthesizeMouseAtCenter(node,
-    {button: 2, type: "contextmenu"}, win);
+    {button: 2, type: "contextmenu"}, view.styleWindow);
   yield onPopup;
 
-  is(view.menuitemCopy.hidden, true, "Copy hidden is as expected: true");
+  is(view._contextmenu.menuitemCopy.hidden, true, "Copy hidden is as expected: true");
 
-  is(view.menuitemCopyLocation.hidden,
+  is(view._contextmenu.menuitemCopyLocation.hidden,
      hidden.copyLocation,
      "Copy Location hidden attribute is as expected: " +
      hidden.copyLocation);
 
-  is(view.menuitemCopyPropertyDeclaration.hidden,
+  is(view._contextmenu.menuitemCopyPropertyDeclaration.hidden,
      hidden.copyPropertyDeclaration,
      "Copy Property Declaration hidden attribute is as expected: " +
      hidden.copyPropertyDeclaration);
 
-  is(view.menuitemCopyPropertyName.hidden,
+  is(view._contextmenu.menuitemCopyPropertyName.hidden,
      hidden.copyPropertyName,
      "Copy Property Name hidden attribute is as expected: " +
      hidden.copyPropertyName);
 
-  is(view.menuitemCopyPropertyValue.hidden,
+  is(view._contextmenu.menuitemCopyPropertyValue.hidden,
      hidden.copyPropertyValue,
      "Copy Property Value hidden attribute is as expected: " +
      hidden.copyPropertyValue);
 
-  is(view.menuitemCopySelector.hidden,
+  is(view._contextmenu.menuitemCopySelector.hidden,
      hidden.copySelector,
      "Copy Selector hidden attribute is as expected: " +
      hidden.copySelector);
 
-  is(view.menuitemCopyRule.hidden,
+  is(view._contextmenu.menuitemCopyRule.hidden,
      hidden.copyRule,
      "Copy Rule hidden attribute is as expected: " +
      hidden.copyRule);
 
   try {
     yield waitForClipboard(() => menuItem.click(),
       () => checkClipboardData(expectedPattern));
   } catch(e) {
     failedClipboard(expectedPattern);
   }
 
-  view._contextmenu.hidePopup();
+  view._contextmenu._menupopup.hidePopup();
 }
 
 function* disableProperty(view) {
   let ruleEditor = getRuleViewRuleEditor(view, 1);
   let propEditor = ruleEditor.rule.textProps[0].editor;
 
   info("Disabling a property");
   propEditor.enable.click();
--- a/browser/devtools/styleinspector/test/browser_ruleview_edit-property-commit.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property-commit.js
@@ -61,17 +61,17 @@ function* runTestData(view, {value, comm
 
   info("Focusing the inplace editor field");
 
   let editor = yield focusEditableField(view, propEditor.valueSpan);
   is(inplaceEditor(propEditor.valueSpan), editor, "Focused editor should be the value span.");
 
   info("Entering test data " + value);
   let onRuleViewChanged = view.once("ruleview-changed");
-  EventUtils.sendString(value, view.doc.defaultView);
+  EventUtils.sendString(value, view.styleWindow);
 
   info("Waiting for focus on the field");
   let onBlur = once(editor.input, "blur");
 
   info("Entering the commit key " + commitKey + " " + modifiers);
   EventUtils.synthesizeKey(commitKey, modifiers);
   yield onBlur;
   // No matter if we escape or commit the change, the preview throttle is going
--- a/browser/devtools/styleinspector/test/browser_ruleview_edit-property-computed.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property-computed.js
@@ -30,17 +30,17 @@ function* editAndCheck(view) {
 
   info("Focusing the inplace editor field");
   let editor = yield focusEditableField(view, propEditor.valueSpan);
   is(inplaceEditor(propEditor.valueSpan), editor, "Focused editor should be the value span.");
 
   let onPropertyChange = waitForComputedStyleProperty("#testid", null, "padding-top", newPaddingValue);
 
   info("Entering a new value");
-  EventUtils.sendString(newPaddingValue, view.doc.defaultView);
+  EventUtils.sendString(newPaddingValue, view.styleWindow);
 
   info("Waiting for the throttled previewValue to apply the changes to document");
   yield onPropertyChange;
 
   let onBlur = once(editor.input, "blur");
 
   info("Entering the commit key and finishing edit");
   EventUtils.synthesizeKey("VK_RETURN", {});
--- a/browser/devtools/styleinspector/test/browser_ruleview_edit-property-increments.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property-increments.js
@@ -168,17 +168,17 @@ function* testIncrement(editor, options,
   is(input.value, options.start, "Value initialized at " + options.start);
 
   let onRuleViewChanged = view.once("ruleview-changed");
   let onKeyUp = once(input, "keyup");
   let key;
   key = options.down ? "VK_DOWN" : "VK_UP";
   key = options.pageDown ? "VK_PAGE_DOWN" : options.pageUp ? "VK_PAGE_UP" : key;
   EventUtils.synthesizeKey(key, {altKey: options.alt, shiftKey: options.shift},
-    view.doc.defaultView);
+    view.styleWindow);
   yield onKeyUp;
   // Only expect a change if the value actually changed!
   if (options.start !== options.end) {
     yield onRuleViewChanged;
   }
 
   is(input.value, options.end, "Value changed to " + options.end);
 }
--- a/browser/devtools/styleinspector/test/browser_ruleview_edit-property_02.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_02.js
@@ -36,60 +36,60 @@ function* testEditProperty(inspector, ru
 
   let editor = yield focusEditableField(ruleView, propEditor.nameSpan);
   let input = editor.input;
   is(inplaceEditor(propEditor.nameSpan), editor, "Next focused editor should be the name editor.");
 
   ok(input.selectionStart === 0 && input.selectionEnd === input.value.length, "Editor contents are selected.");
 
   // Try clicking on the editor's input again, shouldn't cause trouble (see bug 761665).
-  EventUtils.synthesizeMouse(input, 1, 1, {}, ruleView.doc.defaultView);
+  EventUtils.synthesizeMouse(input, 1, 1, {}, ruleView.styleWindow);
   input.select();
 
   info("Entering property name \"border-color\" followed by a colon to focus the value");
   let onFocus = once(idRuleEditor.element, "focus", true);
-  EventUtils.sendString("border-color:", ruleView.doc.defaultView);
+  EventUtils.sendString("border-color:", ruleView.styleWindow);
   yield onFocus;
   yield idRuleEditor.rule._applyingModifications;
 
   info("Verifying that the focused field is the valueSpan");
-  editor = inplaceEditor(ruleView.doc.activeElement);
+  editor = inplaceEditor(ruleView.styleDocument.activeElement);
   input = editor.input;
   is(inplaceEditor(propEditor.valueSpan), editor, "Focus should have moved to the value.");
   ok(input.selectionStart === 0 && input.selectionEnd === input.value.length, "Editor contents are selected.");
 
   info("Entering a value following by a semi-colon to commit it");
   let onBlur = once(editor.input, "blur");
   // Use sendChar() to pass each character as a string so that we can test propEditor.warning.hidden after each character.
   for (let ch of "red;") {
-    EventUtils.sendChar(ch, ruleView.doc.defaultView);
+    EventUtils.sendChar(ch, ruleView.styleWindow);
     is(propEditor.warning.hidden, true,
       "warning triangle is hidden or shown as appropriate");
   }
   yield onBlur;
   yield idRuleEditor.rule._applyingModifications;
 
   let newValue = yield executeInContent("Test:GetRulePropertyValue", {
     styleSheetIndex: 0,
     ruleIndex: 0,
     name: "border-color"
   });
   is(newValue, "red", "border-color should have been set.");
 
   info("Entering property name \"color\" followed by a colon to focus the value");
   onFocus = once(idRuleEditor.element, "focus", true);
-  EventUtils.sendString("color:", ruleView.doc.defaultView);
+  EventUtils.sendString("color:", ruleView.styleWindow);
   yield onFocus;
 
   info("Verifying that the focused field is the valueSpan");
-  editor = inplaceEditor(ruleView.doc.activeElement);
+  editor = inplaceEditor(ruleView.styleDocument.activeElement);
 
   info("Entering a value following by a semi-colon to commit it");
   onBlur = once(editor.input, "blur");
-  EventUtils.sendString("red;", ruleView.doc.defaultView);
+  EventUtils.sendString("red;", ruleView.styleWindow);
   yield onBlur;
   yield idRuleEditor.rule._applyingModifications;
 
   let props = ruleView.element.querySelectorAll(".ruleview-property");
   for (let i = 0; i < props.length; i++) {
     is(props[i].hasAttribute("dirty"), i <= 1,
       "props[" + i + "] marked dirty as appropriate");
   }
--- a/browser/devtools/styleinspector/test/browser_ruleview_edit-selector-commit.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-selector-commit.js
@@ -82,17 +82,17 @@ function* runTestData(inspector, view, d
       "The selector editor got focused");
 
   info("Enter the new selector value: " + value);
   editor.input.value = value;
 
   info("Entering the commit key " + commitKey + " " + modifiers);
   EventUtils.synthesizeKey(commitKey, modifiers);
 
-  let activeElement = view.doc.activeElement;
+  let activeElement = view.styleDocument.activeElement;
 
   if (commitKey === "VK_ESCAPE") {
     is(idRuleEditor.rule.selectorText, expected,
         "Value is as expected: " + expected);
     is(idRuleEditor.isEditing, false, "Selector is not being edited.");
     is(idRuleEditor.selectorText, activeElement,
        "Focus is on selector span.");
     return;
--- a/browser/devtools/styleinspector/test/browser_ruleview_edit-selector_04.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-selector_04.js
@@ -30,17 +30,17 @@ add_task(function*() {
 
 function* testSelectorHighlight(view, name) {
   info("Test creating selector highlighter");
 
   info("Clicking on a selector icon");
   let icon = getRuleViewSelectorHighlighterIcon(view, name);
 
   let onToggled = view.once("ruleview-selectorhighlighter-toggled");
-  EventUtils.synthesizeMouseAtCenter(icon, {}, view.doc.defaultView);
+  EventUtils.synthesizeMouseAtCenter(icon, {}, view.styleWindow);
   let isVisible = yield onToggled;
 
   ok(view.selectorHighlighter, "The selectorhighlighter instance was created");
   ok(isVisible, "The toggle event says the highlighter is visible");
 }
 
 function* testEditSelector(view, name) {
   info("Test editing existing selector fields");
--- a/browser/devtools/styleinspector/test/browser_ruleview_edit-selector_05.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-selector_05.js
@@ -77,22 +77,22 @@ function* testAddProperty(view) {
   let input = editor.input;