Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Thu, 18 Oct 2012 00:09:17 -0700
changeset 110986 9b5bd852aec9a5ae32f0de09ca822690ef851d81
parent 110985 be6a3e7672d7e050f1160b2eaa59c187bc16311e (current diff)
parent 110609 cb573b9307e5f2c48a29973fd9ff69e6dcf8ef6c (diff)
child 110987 5f938395b062c76f6eae3e512d57e5df48f8bfdc
push id23725
push userrnewman@mozilla.com
push dateMon, 22 Oct 2012 19:47:10 +0000
treeherdermozilla-central@acf7f73e8b18 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone19.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to s-c.
intl/uconv/tests/unit/test_decode_x_mac_roman.js
intl/uconv/tests/unit/test_encode_x_mac_roman.js
mobile/android/base/sync/log/writers/SingleTagLogWriter.java
toolkit/components/osfile/osfileutils.cpp
toolkit/components/osfile/osfileutils.h
--- a/Makefile.in
+++ b/Makefile.in
@@ -43,18 +43,20 @@ ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 tier_base_dirs += \
   other-licenses/android \
   $(NULL)
 endif
 
 ifdef MOZ_MEMORY
 tier_base_dirs += memory/mozjemalloc
 ifdef MOZ_JEMALLOC
+ifndef MOZ_NATIVE_JEMALLOC
 tier_base_dirs += memory/jemalloc
 endif
+endif
 tier_base_dirs += memory/build
 endif
 ifndef MOZ_NATIVE_ZLIB
 tier_base_dirs += modules/zlib
 endif
 tier_base_dirs += \
   mozglue \
   memory/mozalloc \
--- a/accessible/src/base/nsTextEquivUtils.cpp
+++ b/accessible/src/base/nsTextEquivUtils.cpp
@@ -27,17 +27,17 @@ nsTextEquivUtils::GetNameFromSubtree(Acc
                                      nsAString& aName)
 {
   aName.Truncate();
 
   if (gInitiatorAcc)
     return NS_OK;
 
   gInitiatorAcc = aAccessible;
-  if (GetRoleRule(aAccessible->Role()) == eFromSubtree) {
+  if (IsNameFromSubtreeAllowed(aAccessible)) {
     //XXX: is it necessary to care the accessible is not a document?
     if (aAccessible->IsContent()) {
       nsAutoString name;
       AppendFromAccessibleChildren(aAccessible, &name);
       name.CompressWhitespace();
       if (!IsWhitespaceString(name))
         aName = name;
     }
--- a/accessible/src/base/nsTextEquivUtils.h
+++ b/accessible/src/base/nsTextEquivUtils.h
@@ -84,16 +84,24 @@ public:
    * node or html:br) and appends it to the given string.
    *
    * @param aContent       [in] the text content
    * @param aString        [in, out] the string
    */
   static nsresult AppendTextEquivFromTextContent(nsIContent *aContent,
                                                  nsAString *aString);
 
+  /**
+   * Return true if the given accessible allows name from subtree.
+   */
+  static bool IsNameFromSubtreeAllowed(Accessible* aAccessible)
+  {
+    return GetRoleRule(aAccessible->Role()) == eFromSubtree;
+  }
+
 private:
   /**
    * Iterates accessible children and calculates text equivalent from each
    * child.
    */
   static nsresult AppendFromAccessibleChildren(Accessible* aAccessible,
                                                nsAString *aString);
   
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -270,37 +270,35 @@ Accessible::Name(nsString& aName)
   if (xblAccessible) {
     xblAccessible->GetAccessibleName(aName);
     if (!aName.IsEmpty())
       return eNameOK;
   }
 
   ENameValueFlag nameFlag = NativeName(aName);
   if (!aName.IsEmpty())
-    return eNameOK;
+    return nameFlag;
 
   // In the end get the name from tooltip.
   if (mContent->IsHTML()) {
     if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
       aName.CompressWhitespace();
       return eNameFromTooltip;
     }
   } else if (mContent->IsXUL()) {
     if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
       aName.CompressWhitespace();
       return eNameFromTooltip;
     }
-  } else {
-    return eNameOK;
   }
 
   if (nameFlag != eNoNameOnPurpose)
     aName.SetIsVoid(true);
 
-  return eNameOK;
+  return nameFlag;
 }
 
 NS_IMETHODIMP
 Accessible::GetDescription(nsAString& aDescription)
 {
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
@@ -1050,44 +1048,47 @@ Accessible::TakeFocus()
   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(focusContent));
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
   if (fm)
     fm->SetFocus(element, 0);
 
   return NS_OK;
 }
 
-void
+ENameValueFlag
 Accessible::GetHTMLName(nsString& aLabel)
 {
   Accessible* labelAcc = nullptr;
   HTMLLabelIterator iter(Document(), this);
   while ((labelAcc = iter.Next())) {
     nsTextEquivUtils::AppendTextEquivFromContent(this, labelAcc->GetContent(),
                                                  &aLabel);
     aLabel.CompressWhitespace();
   }
 
-  if (aLabel.IsEmpty())
-    nsTextEquivUtils::GetNameFromSubtree(this, aLabel);
+  if (!aLabel.IsEmpty())
+    return eNameOK;
+
+  nsTextEquivUtils::GetNameFromSubtree(this, aLabel);
+  return aLabel.IsEmpty() ? eNameOK : eNameFromSubtree;
 }
 
 /**
   * 3 main cases for XUL Controls to be labeled
   *   1 - control contains label="foo"
   *   2 - control has, as a child, a label element
   *        - label has either value="foo" or children
   *   3 - non-child label contains control="controlID"
   *        - label has either value="foo" or children
   * Once a label is found, the search is discontinued, so a control
   *  that has a label child as well as having a label external to
   *  the control that uses the control="controlID" syntax will use
   *  the child label for its Name.
   */
-void
+ENameValueFlag
 Accessible::GetXULName(nsString& aName)
 {
   // CASE #1 (via label attribute) -- great majority of the cases
   nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl =
     do_QueryInterface(mContent);
   if (labeledEl) {
     labeledEl->GetLabel(aName);
   } else {
@@ -1120,35 +1121,35 @@ Accessible::GetXULName(nsString& aName)
         // If no value attribute, a non-empty label must contain
         // children that define its text -- possibly using HTML
         nsTextEquivUtils::
           AppendTextEquivFromContent(this, labelAcc->GetContent(), &aName);
       }
     }
   }
 
-  // XXX If CompressWhiteSpace worked on nsAString we could avoid a copy
   aName.CompressWhitespace();
   if (!aName.IsEmpty())
-    return;
+    return eNameOK;
 
   // Can get text from title of <toolbaritem> if we're a child of a <toolbaritem>
   nsIContent *bindingParent = mContent->GetBindingParent();
   nsIContent *parent = bindingParent? bindingParent->GetParent() :
                                       mContent->GetParent();
   while (parent) {
     if (parent->Tag() == nsGkAtoms::toolbaritem &&
         parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
       aName.CompressWhitespace();
-      return;
+      return eNameOK;
     }
     parent = parent->GetParent();
   }
 
   nsTextEquivUtils::GetNameFromSubtree(this, aName);
+  return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
 }
 
 nsresult
 Accessible::HandleAccEvent(AccEvent* aEvent)
 {
   NS_ENSURE_ARG_POINTER(aEvent);
 
   nsCOMPtr<nsIObserverService> obsService =
@@ -1224,16 +1225,23 @@ Accessible::GetAttributes(nsIPersistentP
     GetValue(valuetext);
     attributes->SetStringProperty(NS_LITERAL_CSTRING("valuetext"), valuetext, oldValueUnused);
   }
 
   // Expose checkable object attribute if the accessible has checkable state
   if (State() & states::CHECKABLE)
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::checkable, NS_LITERAL_STRING("true"));
 
+  // Expose 'explicit-name' attribute.
+  if (!nsTextEquivUtils::IsNameFromSubtreeAllowed(this) ||
+      Name(oldValueUnused) != eNameFromSubtree) {
+    attributes->SetStringProperty(NS_LITERAL_CSTRING("explicit-name"),
+                                  NS_LITERAL_STRING("true"), oldValueUnused);
+  }
+
   // Group attributes (level/setsize/posinset)
   GroupPos groupPos = GroupPosition();
   nsAccUtils::SetAccGroupAttrs(attributes, groupPos.level,
                                groupPos.setSize, groupPos.posInSet);
 
   // Expose object attributes from ARIA attributes.
   aria::AttrIterator attribIter(mContent);
   nsAutoString name, value;
@@ -2415,44 +2423,40 @@ Accessible::Shutdown()
   if (mParent)
     mParent->RemoveChild(this);
 
   nsAccessNodeWrap::Shutdown();
 }
 
 // Accessible protected
 void
-Accessible::ARIAName(nsAString& aName)
+Accessible::ARIAName(nsString& aName)
 {
-  nsAutoString label;
-
   // aria-labelledby now takes precedence over aria-label
   nsresult rv = nsTextEquivUtils::
-    GetTextEquivFromIDRefs(this, nsGkAtoms::aria_labelledby, label);
+    GetTextEquivFromIDRefs(this, nsGkAtoms::aria_labelledby, aName);
   if (NS_SUCCEEDED(rv)) {
-    label.CompressWhitespace();
-    aName = label;
+    aName.CompressWhitespace();
   }
 
-  if (label.IsEmpty() &&
-      mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_label,
-                        label)) {
-    label.CompressWhitespace();
-    aName = label;
+  if (aName.IsEmpty() &&
+      mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_label, aName)) {
+    aName.CompressWhitespace();
   }
 }
 
 // Accessible protected
 ENameValueFlag
 Accessible::NativeName(nsString& aName)
 {
   if (mContent->IsHTML())
-    GetHTMLName(aName);
-  else if (mContent->IsXUL())
-    GetXULName(aName);
+    return GetHTMLName(aName);
+
+  if (mContent->IsXUL())
+    return GetXULName(aName);
 
   return eNameOK;
 }
 
 // Accessible protected
 void
 Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent)
 {
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -54,16 +54,21 @@ enum ENameValueFlag {
 
  /**
   * Name was left empty by the author on purpose:
   * name.IsEmpty() && !name.IsVoid().
   */
  eNoNameOnPurpose,
 
  /**
+  * Name was computed from the subtree.
+  */
+ eNameFromSubtree,
+
+ /**
   * Tooltip was used as a name.
   */
  eNameFromTooltip
 };
 
 /**
  * Group position (level, position in set and set size).
  */
@@ -800,23 +805,23 @@ protected:
    * Return the accessible name provided by native markup. It doesn't take
    * into account ARIA markup used to specify the name.
    */
   virtual mozilla::a11y::ENameValueFlag NativeName(nsString& aName);
 
   /**
    * Returns the accessible name specified by ARIA.
    */
-  void ARIAName(nsAString& aName);
+  void ARIAName(nsString& aName);
 
   /**
    * Compute the name of HTML/XUL node.
    */
-  void GetHTMLName(nsString& aName);
-  void GetXULName(nsString& aName);
+  mozilla::a11y::ENameValueFlag GetHTMLName(nsString& aName);
+  mozilla::a11y::ENameValueFlag GetXULName(nsString& aName);
 
   // helper method to verify frames
   static nsresult GetFullKeyName(const nsAString& aModifierName, const nsAString& aKeyName, nsAString& aStringOut);
 
   /**
    * Return an accessible for the given DOM node, or if that node isn't
    * accessible, return the accessible for the next DOM node which has one
    * (based on forward depth first search).
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -1948,22 +1948,24 @@ HyperTextAccessible::ScrollSubstringToPo
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible public
 
 // Accessible protected
 ENameValueFlag
 HyperTextAccessible::NativeName(nsString& aName)
 {
-  AccessibleWrap::NativeName(aName);
+  ENameValueFlag nameFlag = AccessibleWrap::NativeName(aName);
+  if (!aName.IsEmpty())
+    return nameFlag;
 
   // Get name from title attribute for HTML abbr and acronym elements making it
   // a valid name from markup. Otherwise their name isn't picked up by recursive
   // name computation algorithm. See NS_OK_NAME_FROM_TOOLTIP.
-  if (aName.IsEmpty() && IsAbbreviation() &&
+  if (IsAbbreviation() &&
       mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName))
     aName.CompressWhitespace();
 
   return eNameOK;
 }
 
 void
 HyperTextAccessible::InvalidateChildren()
--- a/accessible/src/generic/ImageAccessible.cpp
+++ b/accessible/src/generic/ImageAccessible.cpp
@@ -72,26 +72,25 @@ ImageAccessible::NativeState()
 ENameValueFlag
 ImageAccessible::NativeName(nsString& aName)
 {
   bool hasAltAttrib =
     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName);
   if (!aName.IsEmpty())
     return eNameOK;
 
-  Accessible::NativeName(aName);
-  if (aName.IsEmpty() && hasAltAttrib) {
-    // No accessible name but empty 'alt' attribute is present. If further name
-    // computation algorithm doesn't provide non empty name then it means
-    // an empty 'alt' attribute was used to indicate a decorative image (see
-    // nsIAccessible::name attribute for details).
-    return eNoNameOnPurpose;
-  }
+  ENameValueFlag nameFlag = Accessible::NativeName(aName);
+  if (!aName.IsEmpty())
+    return nameFlag;
 
-  return eNameOK;
+  // No accessible name but empty 'alt' attribute is present. If further name
+  // computation algorithm doesn't provide non empty name then it means
+  // an empty 'alt' attribute was used to indicate a decorative image (see
+  // Accessible::Name() method for details).
+  return hasAltAttrib ? eNoNameOnPurpose : eNameOK;
 }
 
 role
 ImageAccessible::NativeRole()
 {
   return roles::GRAPHIC;
 }
 
--- a/accessible/src/html/HTMLElementAccessibles.cpp
+++ b/accessible/src/html/HTMLElementAccessibles.cpp
@@ -53,17 +53,17 @@ HTMLBRAccessible::NativeName(nsString& a
 ////////////////////////////////////////////////////////////////////////////////
 
 NS_IMPL_ISUPPORTS_INHERITED0(HTMLLabelAccessible, HyperTextAccessible)
 
 ENameValueFlag
 HTMLLabelAccessible::NativeName(nsString& aName)
 {
   nsTextEquivUtils::GetNameFromSubtree(this, aName);
-  return eNameOK;
+  return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
 }
 
 role
 HTMLLabelAccessible::NativeRole()
 {
   return roles::LABEL;
 }
 
--- a/accessible/src/html/HTMLFormControlAccessible.cpp
+++ b/accessible/src/html/HTMLFormControlAccessible.cpp
@@ -254,23 +254,23 @@ role
 HTMLButtonAccessible::NativeRole()
 {
   return roles::PUSHBUTTON;
 }
 
 ENameValueFlag
 HTMLButtonAccessible::NativeName(nsString& aName)
 {
-  Accessible::NativeName(aName);
+  ENameValueFlag nameFlag = Accessible::NativeName(aName);
   if (!aName.IsEmpty() || mContent->Tag() != nsGkAtoms::input)
-    return eNameOK;
+    return nameFlag;
 
-  // No name from HTML or ARIA
-  if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName) &&
-      !mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName)) {
+  // Note: No need to check @value attribute since it results in anonymous text
+  // node. The name is calculated from subtree in this case.
+  if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName)) {
     // Use the button's (default) label if nothing else works
     nsIFrame* frame = GetFrame();
     if (frame) {
       nsIFormControlFrame* fcFrame = do_QueryFrame(frame);
       if (fcFrame)
         fcFrame->GetFormProperty(nsGkAtoms::defaultLabel, aName);
     }
   }
@@ -318,19 +318,19 @@ HTMLTextFieldAccessible::NativeRole()
   }
   
   return roles::ENTRY;
 }
 
 ENameValueFlag
 HTMLTextFieldAccessible::NativeName(nsString& aName)
 {
-  Accessible::NativeName(aName);
+  ENameValueFlag nameFlag = Accessible::NativeName(aName);
   if (!aName.IsEmpty())
-    return eNameOK;
+    return nameFlag;
 
   if (mContent->GetBindingParent()) {
     // XXX: bug 459640
     // There's a binding parent.
     // This means we're part of another control, so use parent accessible for name.
     // This ensures that a textbox inside of a XUL widget gets
     // an accessible name.
     Accessible* parent = Parent();
@@ -608,19 +608,19 @@ HTMLGroupboxAccessible::GetLegend()
   }
 
   return nullptr;
 }
 
 ENameValueFlag
 HTMLGroupboxAccessible::NativeName(nsString& aName)
 {
-  Accessible::NativeName(aName);
+  ENameValueFlag nameFlag = Accessible::NativeName(aName);
   if (!aName.IsEmpty())
-    return eNameOK;
+    return nameFlag;
 
   nsIContent* legendContent = GetLegend();
   if (legendContent)
     nsTextEquivUtils::AppendTextEquivFromContent(this, legendContent, &aName);
 
   return eNameOK;
 }
 
@@ -691,19 +691,19 @@ role
 HTMLFigureAccessible::NativeRole()
 {
   return roles::FIGURE;
 }
 
 ENameValueFlag
 HTMLFigureAccessible::NativeName(nsString& aName)
 {
-  HyperTextAccessibleWrap::NativeName(aName);
+  ENameValueFlag nameFlag = HyperTextAccessibleWrap::NativeName(aName);
   if (!aName.IsEmpty())
-    return eNameOK;
+    return nameFlag;
 
   nsIContent* captionContent = Caption();
   if (captionContent)
     nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &aName);
 
   return eNameOK;
 }
 
--- a/accessible/src/html/HTMLImageMapAccessible.cpp
+++ b/accessible/src/html/HTMLImageMapAccessible.cpp
@@ -158,19 +158,19 @@ HTMLAreaAccessible::
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLAreaAccessible: nsIAccessible
 
 ENameValueFlag
 HTMLAreaAccessible::NativeName(nsString& aName)
 {
-  Accessible::NativeName(aName);
+  ENameValueFlag nameFlag = Accessible::NativeName(aName);
   if (!aName.IsEmpty())
-    return eNameOK;
+    return nameFlag;
 
   if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName))
     GetValue(aName);
 
   return eNameOK;
 }
 
 void
--- a/accessible/src/html/HTMLSelectAccessible.cpp
+++ b/accessible/src/html/HTMLSelectAccessible.cpp
@@ -195,16 +195,17 @@ HTMLSelectOptionAccessible::NativeName(n
     return eNameOK;
 
   // CASE #2 -- no label parameter, get the first child, 
   // use it if it is a text node
   nsIContent* text = mContent->GetFirstChild();
   if (text && text->IsNodeOfType(nsINode::eTEXT)) {
     nsTextEquivUtils::AppendTextEquivFromTextContent(text, &aName);
     aName.CompressWhitespace();
+    return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
   }
 
   return eNameOK;
 }
 
 uint64_t
 HTMLSelectOptionAccessible::NativeState()
 {
--- a/accessible/src/html/HTMLTableAccessible.cpp
+++ b/accessible/src/html/HTMLTableAccessible.cpp
@@ -383,19 +383,19 @@ uint64_t
 HTMLTableAccessible::NativeState()
 {
   return Accessible::NativeState() | states::READONLY;
 }
 
 ENameValueFlag
 HTMLTableAccessible::NativeName(nsString& aName)
 {
-  Accessible::NativeName(aName);
+  ENameValueFlag nameFlag = Accessible::NativeName(aName);
   if (!aName.IsEmpty())
-    return eNameOK;
+    return nameFlag;
 
   // Use table caption as a name.
   Accessible* caption = Caption();
   if (caption) {
     nsIContent* captionContent = caption->GetContent();
     if (captionContent) {
       nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &aName);
       if (!aName.IsEmpty())
--- a/accessible/src/jsat/Presenters.jsm
+++ b/accessible/src/jsat/Presenters.jsm
@@ -124,31 +124,33 @@ VisualPresenter.prototype = {
   type: 'Visual',
 
   /**
    * The padding in pixels between the object and the highlight border.
    */
   BORDER_PADDING: 2,
 
   viewportChanged: function VisualPresenter_viewportChanged(aWindow) {
-    if (this._currentContext)
+    if (this._currentAccessible) {
+      let context = new PresenterContext(this._currentAccessible);
       return {
         type: this.type,
         details: {
           method: 'show',
-          bounds: this._currentContext.bounds,
+          bounds: context.bounds,
           padding: this.BORDER_PADDING
         }
       };
+    }
 
     return null;
   },
 
   pivotChanged: function VisualPresenter_pivotChanged(aContext, aReason) {
-    this._currentContext = aContext;
+    this._currentAccessible = aContext.accessible;
 
     if (!aContext.accessible)
       return {type: this.type, details: {method: 'hide'}};
 
     try {
       aContext.accessible.scrollTo(
         Ci.nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE);
       return {
--- a/accessible/src/xul/XULElementAccessibles.cpp
+++ b/accessible/src/xul/XULElementAccessibles.cpp
@@ -120,20 +120,21 @@ XULLinkAccessible::Value(nsString& aValu
 
   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, aValue);
 }
 
 ENameValueFlag
 XULLinkAccessible::NativeName(nsString& aName)
 {
   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName);
-  if (aName.IsEmpty())
-    nsTextEquivUtils::GetNameFromSubtree(this, aName);
+  if (!aName.IsEmpty())
+    return eNameOK;
 
-  return eNameOK;
+  nsTextEquivUtils::GetNameFromSubtree(this, aName);
+  return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
 }
 
 role
 XULLinkAccessible::NativeRole()
 {
   return roles::LINK;
 }
 
--- a/accessible/src/xul/XULListboxAccessible.cpp
+++ b/accessible/src/xul/XULListboxAccessible.cpp
@@ -629,18 +629,17 @@ XULListitemAccessible::NativeName(nsStri
   if (childContent) {
     if (childContent->NodeInfo()->Equals(nsGkAtoms::listcell,
                                          kNameSpaceID_XUL)) {
       childContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
       return eNameOK;
     }
   }
 
-  GetXULName(aName);
-  return eNameOK;
+  return GetXULName(aName);
 }
 
 role
 XULListitemAccessible::NativeRole()
 {
   Accessible* list = GetListAccessible();
   if (!list) {
     NS_ERROR("No list accessible for listitem accessible!");
--- a/accessible/tests/mochitest/name/markup.js
+++ b/accessible/tests/mochitest/name/markup.js
@@ -183,16 +183,21 @@ function testNameForAttrRule(aElm, aRule
         name += " ";
 
       name += labelElm.getAttribute("a11yname");
     }
   }
 
   var msg = "Attribute '" + attr + "' test. ";
   testName(aElm, name, msg);
+  if (aRule.getAttribute("explict-name") != "false")
+    testAttrs(aElm, {"explicit-name" : "true"}, true);
+  else
+    testAbsentAttrs(aElm, {"explicit-name" : "true"});
+
   aElm.removeAttribute(attr);
 
   gTestIterator.iterateNext();
 }
 
 function testNameForElmRule(aElm, aRule)
 {
   var labelElm;
@@ -230,16 +235,17 @@ function testNameForElmRule(aElm, aRule)
   if (!labelElm) {
     ok(false, msg + " Failed to find '" + tagname + "' element.");
     gTestIterator.iterateNext();
     return;
   }
 
   var msg = "Element '" + tagname + "' test.";
   testName(aElm, labelElm.getAttribute("a11yname"), msg);
+  testAttrs(aElm, {"explicit-name" : "true"}, true);
 
   var parentNode = labelElm.parentNode;
 
   if (gDumpToConsole) {
     dump("\nProcessed elm rule. Wait for reorder event on " +
          prettyName(parentNode) + "\n");
   }
   waitForEvent(EVENT_REORDER, parentNode,
@@ -247,16 +253,17 @@ function testNameForElmRule(aElm, aRule)
 
   parentNode.removeChild(labelElm);
 }
 
 function testNameForSubtreeRule(aElm, aRule)
 {
   var msg = "From subtree test.";
   testName(aElm, aElm.getAttribute("a11yname"), msg);
+  testAbsentAttrs(aElm, {"explicit-name" : "true"});
 
   if (gDumpToConsole) {
     dump("\nProcessed from subtree rule. Wait for reorder event on " +
          prettyName(aElm) + "\n");
   }
   waitForEvent(EVENT_REORDER, aElm, gTestIterator.iterateNext, gTestIterator);
 
   while (aElm.firstChild)
--- a/accessible/tests/mochitest/name/markuprules.xml
+++ b/accessible/tests/mochitest/name/markuprules.xml
@@ -120,17 +120,17 @@
     <ruleset id="htmlelm">
       <ruleset ref="htmlelm_start"/>
       <ruleset ref="htmlelm_end"/>
     </ruleset>
 
     <!-- specific -->
     <ruleset id="htmlinputbutton">
       <ruleset ref="htmlelm_start"/>
-      <rule attr="value" type="string"/>
+      <rule attr="value" type="string" explict-name="false"/>
       <rule attr="alt" type="string"/>
       <rule attr="src" type="string"/>
       <rule attr="data" type="string"/>
       <ruleset ref="htmlelm_end"/>
     </ruleset>
 
     <ruleset id="htmloption">
       <ruleset ref="aria"/>
--- a/accessible/tests/mochitest/name/test_markup.html
+++ b/accessible/tests/mochitest/name/test_markup.html
@@ -8,16 +8,18 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
   <script type="application/javascript"
           src="../name.js"></script>
+  <script type="application/javascript"
+          src="../attributes.js"></script>
 
   <script type="application/javascript"
           src="markup.js"></script>
 
   <script type="application/javascript">
     // gA11yEventDumpID = "eventdump";
     //gDumpToConsole = true;
     //gA11yEventDumpToConsole = true;
--- a/allmakefiles.sh
+++ b/allmakefiles.sh
@@ -54,17 +54,17 @@ if [ ! "$LIBXUL_SDK" ]; then
       build/stlport/stl/config/_android.h
     "
   fi
   add_makefiles "
     memory/mozalloc/Makefile
     mozglue/Makefile
     mozglue/build/Makefile
   "
-  if [ "$MOZ_JEMALLOC" ]; then
+  if [ "$MOZ_JEMALLOC" -a -z "$MOZ_NATIVE_JEMALLOC" ]; then
     add_makefiles "
       memory/jemalloc/Makefile
     "
   fi
   if [ "$MOZ_MEMORY" ]; then
     add_makefiles "
       memory/mozjemalloc/Makefile
       memory/build/Makefile
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -261,20 +261,21 @@ pref("media.cache_size", 4096);    // 4M
 pref("media.video-queue.default-size", 3);
 
 //  0: don't show fullscreen keyboard
 //  1: always show fullscreen keyboard
 // -1: show fullscreen keyboard based on threshold pref
 pref("widget.ime.android.landscape_fullscreen", -1);
 pref("widget.ime.android.fullscreen_threshold", 250); // in hundreths of inches
 
-// optimize images memory usage
+// optimize images' memory usage
 pref("image.mem.decodeondraw", true);
 pref("content.image.allow_locking", false);
 pref("image.mem.min_discard_timeout_ms", 10000);
+pref("image.mem.max_decoded_image_kb", 5120); /* 5MB */
 
 // enable touch events interfaces
 pref("dom.w3c_touch_events.enabled", true);
 pref("dom.w3c_touch_events.safetyX", 0); // escape borders in units of 1/240"
 pref("dom.w3c_touch_events.safetyY", 120); // escape borders in units of 1/240"
 
 #ifdef MOZ_SAFE_BROWSING
 // Safe browsing does nothing unless this pref is set
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -493,16 +493,20 @@ Services.obs.addObserver(function onSyst
   shell.sendSystemMessage(msg);
 }, 'system-messages-open-app', false);
 
 Services.obs.addObserver(function(aSubject, aTopic, aData) {
   shell.sendChromeEvent({ type: "fullscreenoriginchange",
                           fullscreenorigin: aData });
 }, "fullscreen-origin-change", false);
 
+Services.obs.addObserver(function onWebappsStart(subject, topic, data) {
+  shell.sendChromeEvent({ type: 'webapps-registry-start' });
+}, 'webapps-registry-start', false);
+
 Services.obs.addObserver(function onWebappsReady(subject, topic, data) {
   shell.sendChromeEvent({ type: 'webapps-registry-ready' });
 }, 'webapps-registry-ready', false);
 
 Services.obs.addObserver(function onBluetoothVolumeChange(subject, topic, data) {
   if (data == 'up') {
     shell.sendChromeEvent({ type: 'volume-up-button-press' });
     shell.sendChromeEvent({ type: 'volume-up-button-release' });
--- a/b2g/components/UpdatePrompt.js
+++ b/b2g/components/UpdatePrompt.js
@@ -21,33 +21,42 @@ let log =
 const APPLY_PROMPT_TIMEOUT =
       Services.prefs.getIntPref("b2g.update.apply-prompt-timeout");
 const APPLY_IDLE_TIMEOUT =
       Services.prefs.getIntPref("b2g.update.apply-idle-timeout");
 const SELF_DESTRUCT_TIMEOUT =
       Services.prefs.getIntPref("b2g.update.self-destruct-timeout");
 
 const APPLY_IDLE_TIMEOUT_SECONDS = APPLY_IDLE_TIMEOUT / 1000;
-
+const NETWORK_ERROR_OFFLINE = 111;
 
 XPCOMUtils.defineLazyServiceGetter(Services, "aus",
                                    "@mozilla.org/updates/update-service;1",
                                    "nsIApplicationUpdateService");
 
+XPCOMUtils.defineLazyServiceGetter(Services, "um",
+                                   "@mozilla.org/updates/update-manager;1",
+                                   "nsIUpdateManager");
+
 XPCOMUtils.defineLazyServiceGetter(Services, "idle",
                                    "@mozilla.org/widget/idleservice;1",
                                    "nsIIdleService");
 
+XPCOMUtils.defineLazyServiceGetter(Services, "settings",
+                                   "@mozilla.org/settingsService;1",
+                                   "nsISettingsService");
+
 function UpdatePrompt() {
   this.wrappedJSObject = this;
 }
 
 UpdatePrompt.prototype = {
   classID: Components.ID("{88b3eb21-d072-4e3b-886d-f89d8c49fe59}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt,
+                                         Ci.nsIUpdateCheckListener,
                                          Ci.nsIRequestObserver,
                                          Ci.nsIProgressEventSink,
                                          Ci.nsIObserver]),
   _xpcom_factory: XPCOMUtils.generateSingletonFactory(UpdatePrompt),
 
   _update: null,
   _applyPromptTimer: null,
   _waitingForIdle: false,
@@ -83,67 +92,116 @@ UpdatePrompt.prototype = {
     log("Update is ready to apply, registering idle timeout of " +
         APPLY_IDLE_TIMEOUT_SECONDS + " seconds before prompting.");
 
     this._update = aUpdate;
     this.waitForIdle();
   },
 
   showUpdateError: function UP_showUpdateError(aUpdate) {
-    if (aUpdate.state == "failed") {
-      log("Failed to download update, errorCode: " + aUpdate.errorCode);
-    }
+    log("Update error, state: " + aUpdate.state + ", errorCode: " +
+        aUpdate.errorCode);
+
+    this.sendUpdateEvent("update-error", aUpdate);
+    this.setUpdateStatus(aUpdate.statusText);
   },
 
   showUpdateHistory: function UP_showUpdateHistory(aParent) { },
   showUpdateInstalled: function UP_showUpdateInstalled() { },
 
+  // nsIUpdateCheckListener
+
+  onCheckComplete: function UP_onCheckComplete(request, updates, updateCount) {
+    if (Services.um.activeUpdate) {
+      return;
+    }
+
+    if (updateCount == 0) {
+      this.setUpdateStatus("no-updates");
+      return;
+    }
+
+    let update = Services.aus.selectUpdate(updates, updateCount);
+    if (!update) {
+      this.setUpdateStatus("already-latest-version");
+      return;
+    }
+
+    this.setUpdateStatus("check-complete");
+    this.showUpdateAvailable(update);
+  },
+
+  onError: function UP_onError(request, update) {
+    if (update.errorCode == NETWORK_ERROR_OFFLINE) {
+      this.setUpdateStatus("retry-when-online");
+    }
+
+    Services.aus.QueryInterface(Ci.nsIUpdateCheckListener);
+    Services.aus.onError(request, update);
+  },
+
+  onProgress: function UP_onProgress(request, position, totalSize) {
+    Services.aus.QueryInterface(Ci.nsIUpdateCheckListener);
+    Services.aus.onProgress(request, position, totalSize);
+  },
+
   // Custom functions
 
   waitForIdle: function UP_waitForIdle() {
     if (this._waitingForIdle) {
       return;
     }
 
     this._waitingForIdle = true;
     Services.idle.addIdleObserver(this, APPLY_IDLE_TIMEOUT_SECONDS);
     Services.obs.addObserver(this, "quit-application", false);
   },
 
+  setUpdateStatus: function UP_setUpdateStatus(aStatus) {
+    log("Setting gecko.updateStatus: " + aStatus);
+
+    let lock = Services.settings.createLock();
+    lock.set("gecko.updateStatus", aStatus, null);
+  },
+
   showApplyPrompt: function UP_showApplyPrompt(aUpdate) {
     if (!this.sendUpdateEvent("update-prompt-apply", aUpdate)) {
       log("Unable to prompt, forcing restart");
       this.restartProcess();
       return;
     }
 
     // Schedule a fallback timeout in case the UI is unable to respond or show
     // a prompt for some reason.
     this._applyPromptTimer = this.createTimer(APPLY_PROMPT_TIMEOUT);
   },
 
   sendUpdateEvent: function UP_sendUpdateEvent(aType, aUpdate) {
     let detail = {
       displayVersion: aUpdate.displayVersion,
-      detailsURL: aUpdate.detailsURL
+      detailsURL: aUpdate.detailsURL,
+      statusText: aUpdate.statusText,
+      state: aUpdate.state,
+      errorCode: aUpdate.errorCode,
+      isOSUpdate: aUpdate.isOSUpdate
     };
 
     let patch = aUpdate.selectedPatch;
-    if (!patch) {
+    if (!patch && aUpdate.patchCount > 0) {
       // For now we just check the first patch to get size information if a
       // patch hasn't been selected yet.
-      if (aUpdate.patchCount == 0) {
-        log("Warning: no patches available in update");
-        return false;
-      }
       patch = aUpdate.getPatchAt(0);
     }
 
-    detail.size = patch.size;
-    detail.updateType = patch.type;
+    if (patch) {
+      detail.size = patch.size;
+      detail.updateType = patch.type;
+    } else {
+      log("Warning: no patches available in update");
+    }
 
     this._update = aUpdate;
     return this.sendChromeEvent(aType, detail);
   },
 
   sendChromeEvent: function UP_sendChromeEvent(aType, aDetail) {
     let browser = Services.wm.getMostRecentWindow("navigator:browser");
     if (!browser) {
@@ -247,21 +305,27 @@ UpdatePrompt.prototype = {
       log("Error: Couldn't reboot into recovery to apply FOTA update " +
           aOsUpdatePath);
     }
   },
 
   forceUpdateCheck: function UP_forceUpdateCheck() {
     log("Forcing update check");
 
+    // If we already have an active update available, don't try to
+    // download again, just prompt for install.
+    if (Services.um.activeUpdate) {
+      this.setUpdateStatus("check-complete");
+      this.showApplyPrompt(Services.um.activeUpdate);
+      return;
+    }
+
     let checker = Cc["@mozilla.org/updates/update-checker;1"]
                     .createInstance(Ci.nsIUpdateChecker);
-
-    Services.aus.QueryInterface(Ci.nsIUpdateCheckListener);
-    checker.checkForUpdates(Services.aus, true);
+    checker.checkForUpdates(this, true);
   },
 
   handleEvent: function UP_handleEvent(evt) {
     if (evt.type !== "mozContentEvent") {
       return;
     }
 
     let detail = evt.detail;
--- a/b2g/components/YoutubeProtocolHandler.js
+++ b/b2g/components/YoutubeProtocolHandler.js
@@ -88,17 +88,17 @@ YoutubeProtocolHandler.prototype = {
       streams.forEach(function(aStream) {
         let params = extractParameters(aStream);
         let url = params["url"];
         let type = params["type"] ? params["type"].split(";")[0] : null;
 
         let index;
         if (url && type && ((index = recognizedTypes.indexOf(type)) != -1) &&
             index > bestType) {
-          uri = url;
+          uri = url + '&signature=' + (params["sig"] ? params['sig'] : '');
           mimeType = type;
           bestType = index;
         }
         for (let param in params) {
           if (["thumbnail_url", "length_seconds", "title"].indexOf(param) != -1) {
             extras[param] = decodeURIComponent(params[param]);
           }
         }
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -173,16 +173,34 @@ let SocialUI = {
 
   undoActivation: function SocialUI_undoActivation() {
     Social.active = false;
     this.notificationPanel.hidePopup();
   },
 
   haveLoggedInUser: function SocialUI_haveLoggedInUser() {
     return !!(Social.provider && Social.provider.profile && Social.provider.profile.userName);
+  },
+
+  closeSocialPanelForLinkTraversal: function (target, linkNode) {
+    // No need to close the panel if this traversal was not retargeted
+    if (target == "" || target == "_self")
+      return;
+
+    // Check to see whether this link traversal was in a social panel
+    let win = linkNode.ownerDocument.defaultView;
+    let container = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                                  .getInterface(Ci.nsIWebNavigation)
+                                  .QueryInterface(Ci.nsIDocShell)
+                                  .chromeEventHandler;
+    let containerParent = container.parentNode;
+    if (containerParent.classList.contains("social-panel") &&
+        containerParent instanceof Ci.nsIDOMXULPopupElement) {
+      containerParent.hidePopup();
+    }
   }
 }
 
 let SocialChatBar = {
   get chatbar() {
     return document.getElementById("pinnedchats");
   },
   // Whether the chats can be shown for this window.
@@ -622,17 +640,17 @@ var SocialToolbar = {
   get button() {
     return document.getElementById("social-provider-button");
   },
 
   updateButtonHiddenState: function SocialToolbar_updateButtonHiddenState() {
     let tbi = document.getElementById("social-toolbar-item");
     tbi.hidden = !Social.uiVisible;
     if (!SocialUI.haveLoggedInUser()) {
-      let parent = document.getElementById("social-notification-box");
+      let parent = document.getElementById("social-notification-panel");
       while (parent.hasChildNodes())
         parent.removeChild(parent.firstChild);
 
       while (tbi.lastChild != tbi.firstChild)
         tbi.removeChild(tbi.lastChild);
     }
   },
 
@@ -657,17 +675,16 @@ var SocialToolbar = {
   },
 
   updateButton: function SocialToolbar_updateButton() {
     this.updateButtonHiddenState();
     let provider = Social.provider;
     let icons = provider.ambientNotificationIcons;
     let iconNames = Object.keys(icons);
     let iconBox = document.getElementById("social-toolbar-item");
-    let notifBox = document.getElementById("social-notification-box");
     let panel = document.getElementById("social-notification-panel");
     panel.hidden = false;
 
     let command = document.getElementById("Social:ToggleNotifications");
     command.setAttribute("checked", Services.prefs.getBoolPref("social.toast-notifications.enabled"));
 
     const CACHE_PREF_NAME = "social.cached.notificationIcons";
     // provider.profile == undefined means no response yet from the provider
@@ -775,40 +792,39 @@ var SocialToolbar = {
       let labelValue = icon.counter || "";
       // Only update the value attribute if it has changed to reduce layout changes.
       if (!label.hasAttribute("value") || label.getAttribute("value") != labelValue)
         label.setAttribute("value", labelValue);
 
       if (image.getAttribute("src") != icon.iconURL)
         image.setAttribute("src", icon.iconURL);
     }
-    notifBox.appendChild(notificationFrames);
+    panel.appendChild(notificationFrames);
     iconBox.appendChild(iconContainers);
 
     for (let frame of createdFrames) {
       if (frame.docShell) {
         frame.docShell.isActive = false;
         frame.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIWebProgress)
                       .addProgressListener(new SocialErrorListener("notification-panel"),
                                            Ci.nsIWebProgress.NOTIFY_STATE_REQUEST |
                                            Ci.nsIWebProgress.NOTIFY_LOCATION);
       }
     }
   },
 
   showAmbientPopup: function SocialToolbar_showAmbientPopup(aToolbarButtonBox) {
     let panel = document.getElementById("social-notification-panel");
-    let notifBox = document.getElementById("social-notification-box");
     let notificationFrameId = aToolbarButtonBox.getAttribute("notificationFrameId");
     let notificationFrame = document.getElementById(notificationFrameId);
 
     // Clear dimensions on all browsers so the panel size will
     // only use the selected browser.
-    let frameIter = notifBox.firstElementChild;
+    let frameIter = panel.firstElementChild;
     while (frameIter) {
       frameIter.collapsed = (frameIter != notificationFrame);
       frameIter = frameIter.nextElementSibling;
     }
 
     function dispatchPanelEvent(name) {
       let evt = notificationFrame.contentDocument.createEvent("CustomEvent");
       evt.initCustomEvent(name, true, true, {});
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -373,35 +373,33 @@ window[chromehidden~="toolbar"] toolbar:
 /*  Full Screen UI */
 
 #fullscr-toggler {
   height: 1px;
   background: black;
 }
 
 #full-screen-warning-container {
-  pointer-events: none;
   position: fixed;
   top: 0;
   left: 0;
   width: 100%;
   height: 100%;
 }
 
 #full-screen-warning-container[fade-warning-out] {
   transition-property: opacity !important;
   transition-duration: 500ms !important;
   opacity: 0.0;
 }
 
 #full-screen-warning-message {
-  pointer-events: auto;
   /* We must specify a max-width, otherwise word-wrap:break-word doesn't
      work in descendant <description> and <label> elements. Bug 630864. */
-  max-width: 800px;  
+  max-width: 800px;
 }
 
 #full-screen-domain-text,
 #full-screen-remember-decision > .checkbox-label-box > .checkbox-label {
   word-wrap: break-word;
   /* We must specify a min-width, otherwise word-wrap:break-word doesn't work. Bug 630864. */
   min-width: 1px;
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2544,17 +2544,17 @@ let BrowserOnClick = {
     let elmId = aTargetElm.getAttribute("id");
     let secHistogram = Cc["@mozilla.org/base/telemetry;1"].
                         getService(Ci.nsITelemetry).
                         getHistogramById("SECURITY_UI");
 
     switch (elmId) {
       case "exceptionDialogButton":
         secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_CLICK_ADD_EXCEPTION);
-        let params = { exceptionAdded : false, handlePrivateBrowsing : true };
+        let params = { exceptionAdded : false };
 
         try {
           switch (Services.prefs.getIntPref("browser.ssl_override_behavior")) {
             case 2 : // Pre-fetch & pre-populate
               params.prefetchCert = true;
             case 1 : // Pre-populate
               params.location = aOwnerDoc.location.href;
           }
@@ -3906,16 +3906,22 @@ var XULBrowserWindow = {
       field.label = text;
       field.setAttribute("crop", type == "overLink" ? "center" : "end");
       this.statusText = text;
     }
   },
 
   // Called before links are navigated to to allow us to retarget them if needed.
   onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
+    let target = this._onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
+    SocialUI.closeSocialPanelForLinkTraversal(target, linkNode);
+    return target;
+  },
+
+  _onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
     // Don't modify non-default targets or targets that aren't in top-level app
     // tab docshells (isAppTab will be false for app tab subframes).
     if (originalTarget != "" || !isAppTab)
       return originalTarget;
 
     // External links from within app tabs should always open in new tabs
     // instead of replacing the app tab's page (Bug 575561)
     let linkHost;
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -262,19 +262,17 @@
       </hbox>
     </panel>
 
     <panel id="social-notification-panel"
            class="social-panel"
            type="arrow"
            hidden="true"
            consumeoutsideclicks="true"
-           noautofocus="true">
-      <box id="social-notification-box" flex="1"></box>
-    </panel>
+           noautofocus="true"/>
     <panel id="social-flyout-panel"
            class="social-panel"
            onpopupshown="SocialFlyout.onShown()"
            onpopuphidden="SocialFlyout.onHidden()"
            side="right"
            type="arrow"
            hidden="true"
            noautofocus="true"
--- a/browser/base/content/test/browser_pluginnotification.js
+++ b/browser/base/content/test/browser_pluginnotification.js
@@ -906,10 +906,71 @@ function test21e() {
     var plugin = doc.getElementById(id);
     var rect = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox").getBoundingClientRect();
     ok(rect.width == 0, "Test 21e, Plugin with id=" + plugin.id + " overlay rect should have 0px width after being clicked");
     ok(rect.height == 0, "Test 21e, Plugin with id=" + plugin.id + " overlay rect should have 0px height after being clicked");
     var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
     ok(objLoadingContent.activated, "Test 21e, Plugin with id=" + plugin.id + " should be activated");
   }
 
+  Services.prefs.setBoolPref("plugins.click_to_play", true);
+  prepareTest(test22, gTestRoot + "plugin_test.html");
+}
+
+// Tests that a click-to-play plugin retains its activated state upon reloading
+function test22() {
+  ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser), "Test 22, Should have a click-to-play notification");
+
+  // Plugin should start as CTP
+  var pluginNode = gTestBrowser.contentDocument.getElementById("test");
+  ok(pluginNode, "Test 22, Found plugin in page");
+  var objLoadingContent = pluginNode.QueryInterface(Ci.nsIObjectLoadingContent);
+  is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, "Test 22, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
+
+  // Activate
+  objLoadingContent.playPlugin();
+  is(objLoadingContent.displayedType, Ci.nsIObjectLoadingContent.TYPE_PLUGIN, "Test 22, plugin should have started");
+  ok(pluginNode.activated, "Test 22, plugin should be activated");
+
+  // Reload plugin
+  var oldVal = pluginNode.getObjectValue();
+  pluginNode.src = pluginNode.src;
+  is(objLoadingContent.displayedType, Ci.nsIObjectLoadingContent.TYPE_PLUGIN, "Test 22, Plugin should have retained activated state");
+  ok(pluginNode.activated, "Test 22, plugin should have remained activated");
+  // Sanity, ensure that we actually reloaded the instance, since this behavior might change in the future.
+  var pluginsDiffer;
+  try {
+    pluginNode.checkObjectValue(oldVal);
+  } catch (e) {
+    pluginsDiffer = true;
+  }
+  ok(pluginsDiffer, "Test 22, plugin should have reloaded");
+
+  prepareTest(test23, gTestRoot + "plugin_test.html");
+}
+
+// Tests that a click-to-play plugin resets its activated state when changing types
+function test23() {
+  ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser), "Test 23, Should have a click-to-play notification");
+
+  // Plugin should start as CTP
+  var pluginNode = gTestBrowser.contentDocument.getElementById("test");
+  ok(pluginNode, "Test 23, Found plugin in page");
+  var objLoadingContent = pluginNode.QueryInterface(Ci.nsIObjectLoadingContent);
+  is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, "Test 23, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
+
+  // Activate
+  objLoadingContent.playPlugin();
+  is(objLoadingContent.displayedType, Ci.nsIObjectLoadingContent.TYPE_PLUGIN, "Test 23, plugin should have started");
+  ok(pluginNode.activated, "Test 23, plugin should be activated");
+
+  // Reload plugin (this may need RunSoon() in the future when plugins change state asynchronously)
+  pluginNode.type = null;
+  pluginNode.src = pluginNode.src; // We currently don't properly change state just on type change, bug 767631
+  is(objLoadingContent.displayedType, Ci.nsIObjectLoadingContent.TYPE_NULL, "Test 23, plugin should be unloaded");
+  pluginNode.type = "application/x-test";
+  pluginNode.src = pluginNode.src;
+  is(objLoadingContent.displayedType, Ci.nsIObjectLoadingContent.TYPE_NULL, "Test 23, Plugin should not have activated");
+  is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, "Test 23, Plugin should be click-to-play");
+  ok(!pluginNode.activated, "Test 23, plugin node should not be activated");
+
   finishTest();
 }
--- a/browser/base/content/test/browser_social_flyout.js
+++ b/browser/base/content/test/browser_social_flyout.js
@@ -106,10 +106,45 @@ var tests = {
           }, false);
           is(panel.state, "open", "flyout should be open");
           port.close(); // so we don't get the -visibility message as it hides...
           SocialFlyout.dispatchPanelEvent("socialTest-CloseSelf");
           break;
       }
     }
     port.postMessage({topic: "test-init"});
+  },
+
+  testCloseOnLinkTraversal: function(next) {
+
+    function onTabOpen(event) {
+      gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen, true);
+      is(panel.state, "closed", "flyout should be closed");
+      ok(true, "Link should open a new tab");
+      executeSoon(function(){
+        gBrowser.removeTab(event.target);
+        next();
+      });
+    }
+
+    let panel = document.getElementById("social-flyout-panel");
+    let port = Social.provider.getWorkerPort();
+    ok(port, "provider has a port");
+    port.onmessage = function (e) {
+      let topic = e.data.topic;
+      switch (topic) {
+        case "test-init-done":
+          port.postMessage({topic: "test-flyout-open"});
+          break;
+        case "got-flyout-visibility":
+          if (e.data.result == "shown") {
+            // click on our test link
+            is(panel.state, "open", "flyout should be open");
+            gBrowser.tabContainer.addEventListener("TabOpen", onTabOpen, true); 
+            let iframe = panel.firstChild;
+            iframe.contentDocument.getElementById('traversal').click();
+          }
+          break;
+      }
+    }
+    port.postMessage({topic: "test-init"});
   }
 }
--- a/browser/base/content/test/social_flyout.html
+++ b/browser/base/content/test/social_flyout.html
@@ -26,10 +26,12 @@
         var evt = document.createEvent("CustomEvent");
         evt.initCustomEvent("SocialTest-DoneCloseSelf", true, true, {});
         document.documentElement.dispatchEvent(evt);
       }, false);
     </script>
   </head>
   <body style="max-width: 250px;" onload="pingWorker();">
     <p>This is a test social flyout panel.</p>
+    <a id="traversal" href="http://mochi.test">test link</a>
   </body>
 </html>
+
--- a/browser/components/preferences/in-content/tests/Makefile.in
+++ b/browser/components/preferences/in-content/tests/Makefile.in
@@ -20,14 +20,17 @@ include $(topsrcdir)/config/rules.mk
     browser_connection.js \
     privacypane_tests.js \
     browser_privacypane_1.js \
     browser_privacypane_2.js \
     browser_privacypane_3.js \
     browser_privacypane_4.js \
     browser_privacypane_5.js \
     browser_privacypane_6.js \
-    browser_privacypane_7.js \
     browser_privacypane_8.js \
     $(NULL)
 
+ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+_BROWSER_FILES += browser_privacypane_7.js
+endif
+
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/components/preferences/jar.mn
+++ b/browser/components/preferences/jar.mn
@@ -28,17 +28,17 @@ browser.jar:
 *   content/browser/preferences/languages.xul
     content/browser/preferences/languages.js
 *   content/browser/preferences/main.xul
     content/browser/preferences/main.js
 *   content/browser/preferences/permissions.xul
     content/browser/preferences/permissions.js
 *   content/browser/preferences/preferences.xul
     content/browser/preferences/privacy.xul
-    content/browser/preferences/privacy.js
+*   content/browser/preferences/privacy.js
     content/browser/preferences/sanitize.xul
     content/browser/preferences/security.xul
     content/browser/preferences/security.js
     content/browser/preferences/selectBookmark.xul
     content/browser/preferences/selectBookmark.js
 #ifdef MOZ_SERVICES_SYNC
     content/browser/preferences/sync.xul
     content/browser/preferences/sync.js
--- a/browser/components/preferences/privacy.js
+++ b/browser/components/preferences/privacy.js
@@ -217,28 +217,32 @@ var gPrivacyPane = {
   },
 
   autoStartPrivateBrowsingObserver:
   {
     QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIObserver]),
 
     observe: function PPP_observe(aSubject, aTopic, aData)
     {
-      let privateBrowsingService = Components.classes["@mozilla.org/privatebrowsing;1"].
-        getService(Components.interfaces.nsIPrivateBrowsingService);
-
       // Toggle the private browsing mode without switching the session
       let prefValue = document.getElementById("browser.privatebrowsing.autostart").value;
       let keepCurrentSession = document.getElementById("browser.privatebrowsing.keep_current_session");
       keepCurrentSession.value = true;
+
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+      let privateBrowsingService = Components.classes["@mozilla.org/privatebrowsing;1"].
+        getService(Components.interfaces.nsIPrivateBrowsingService);
+
       // If activating from within the private browsing mode, reset the
       // private session
       if (prefValue && privateBrowsingService.privateBrowsingEnabled)
         privateBrowsingService.privateBrowsingEnabled = false;
       privateBrowsingService.privateBrowsingEnabled = prefValue;
+#endif
+
       keepCurrentSession.reset();
     }
   },
 
   // HISTORY
 
   /**
    * Read the location bar enabled and suggestion prefs
--- a/browser/components/preferences/tests/Makefile.in
+++ b/browser/components/preferences/tests/Makefile.in
@@ -18,16 +18,19 @@ include $(topsrcdir)/config/rules.mk
     browser_bug705422.js \
     privacypane_tests.js \
     browser_privacypane_1.js \
     browser_privacypane_2.js \
     browser_privacypane_3.js \
     browser_privacypane_4.js \
     browser_privacypane_5.js \
     browser_privacypane_6.js \
-    browser_privacypane_7.js \
     browser_privacypane_8.js \
     browser_permissions.js \
     browser_chunk_permissions.js \
     $(NULL)
 
+ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+_BROWSER_FILES += browser_privacypane_7.js
+endif
+
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -461,16 +461,17 @@ PrivateBrowsingService.prototype = {
       case "command-line-startup":
         this._obs.removeObserver(this, "command-line-startup");
         aSubject.QueryInterface(Ci.nsICommandLine);
         if (aSubject.findFlag("private", false) >= 0) {
           // Don't need to go into PB mode if it's already set to autostart
           if (this._autoStarted)
             aSubject.handleFlag("private", false);
 
+          Services.prefs.setBoolPref("browser.privatebrowsing.autostart", true);
           this.privateBrowsingEnabled = true;
           this._autoStarted = true;
           this._lastChangedByCommandLine = true;
         }
         else if (aSubject.findFlag("private-toggle", false) >= 0) {
           this._lastChangedByCommandLine = true;
         }
         break;
--- a/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_certexceptionsui.js
+++ b/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_certexceptionsui.js
@@ -14,64 +14,37 @@ function test() {
   const EXCEPTIONS_DLG_URL = 'chrome://pippki/content/exceptionDialog.xul';
   const EXCEPTIONS_DLG_FEATURES = 'chrome,centerscreen';
   const INVALID_CERT_LOCATION = 'https://nocert.example.com/';
   waitForExplicitFinish();
 
   // enter private browsing mode
   pb.privateBrowsingEnabled = true;
 
-  step1();
+  doTest();
 
-  // Test the certificate exceptions dialog as it is invoked from about:certerror
-  function step1() {
+  // Test the certificate exceptions dialog
+  function doTest() {
     let params = {
       exceptionAdded : false,
       location: INVALID_CERT_LOCATION,
-      handlePrivateBrowsing : true,
       prefetchCert: true,
     };
     function testCheckbox() {
       win.removeEventListener("load", testCheckbox, false);
       Services.obs.addObserver(function (aSubject, aTopic, aData) {
         Services.obs.removeObserver(arguments.callee, "cert-exception-ui-ready", false);
         ok(win.gCert, "The certificate information should be available now");
 
         let checkbox = win.document.getElementById("permanent");
         ok(checkbox.hasAttribute("disabled"),
           "the permanent checkbox should be disabled when handling the private browsing mode");
         ok(!checkbox.hasAttribute("checked"),
           "the permanent checkbox should not be checked when handling the private browsing mode");
         win.close();
-        step2();
-      }, "cert-exception-ui-ready", false);
-    }
-    var win = openDialog(EXCEPTIONS_DLG_URL, "", EXCEPTIONS_DLG_FEATURES, params);
-    win.addEventListener("load", testCheckbox, false);
-  }
-
-  // Test the certificate excetions dialog as it is invoked from the Preferences dialog
-  function step2() {
-    let params = {
-      exceptionAdded : false,
-      location: INVALID_CERT_LOCATION,
-      prefetchCert: true,
-    };
-    function testCheckbox() {
-      win.removeEventListener("load", testCheckbox, false);
-      Services.obs.addObserver(function (aSubject, aTopic, aData) {
-        Services.obs.removeObserver(arguments.callee, "cert-exception-ui-ready", false);
-        ok(win.gCert, "The certificate information should be available now");
-
-        let checkbox = win.document.getElementById("permanent");
-        ok(!checkbox.hasAttribute("disabled"),
-          "the permanent checkbox should not be disabled when not handling the private browsing mode");
-        ok(checkbox.hasAttribute("checked"),
-          "the permanent checkbox should be checked when not handling the private browsing mode");
-        win.close();
         cleanup();
       }, "cert-exception-ui-ready", false);
     }
     var win = openDialog(EXCEPTIONS_DLG_URL, "", EXCEPTIONS_DLG_FEATURES, params);
     win.addEventListener("load", testCheckbox, false);
   }
 
   function cleanup() {
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,4 +1,4 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 0.5.184
+Current extension version is: 0.6.39
 
--- a/browser/extensions/pdfjs/components/PdfStreamConverter.js
+++ b/browser/extensions/pdfjs/components/PdfStreamConverter.js
@@ -26,40 +26,46 @@ const Cu = Components.utils;
 // True only if this is the version of pdf.js that is included with firefox.
 const MOZ_CENTRAL = true;
 const PDFJS_EVENT_ID = 'pdf.js.message';
 const PDF_CONTENT_TYPE = 'application/pdf';
 const PREF_PREFIX = 'pdfjs';
 const PDF_VIEWER_WEB_PAGE = 'resource://pdf.js/web/viewer.html';
 const MAX_DATABASE_LENGTH = 4096;
 const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
-const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
-const METRO_ID = '{99bceaaa-e3c6-48c1-b981-ef9b46b67d60}';
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://gre/modules/NetUtil.jsm');
 
 
 let appInfo = Cc['@mozilla.org/xre/app-info;1']
                   .getService(Ci.nsIXULAppInfo);
-let privateBrowsing, inPrivateBrowsing;
 let Svc = {};
 XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
                                    '@mozilla.org/mime;1',
                                    'nsIMIMEService');
 
+let isInPrivateBrowsing;
 if (appInfo.ID === FIREFOX_ID) {
-  privateBrowsing = Cc['@mozilla.org/privatebrowsing;1']
-                          .getService(Ci.nsIPrivateBrowsingService);
-  inPrivateBrowsing = privateBrowsing.privateBrowsingEnabled;
-} else if (appInfo.ID === SEAMONKEY_ID ||
-           appInfo.ID === METRO_ID) {
-  privateBrowsing = null;
-  inPrivateBrowsing = false;
+  let privateBrowsing = Cc['@mozilla.org/privatebrowsing;1']
+                            .getService(Ci.nsIPrivateBrowsingService);
+  isInPrivateBrowsing = function getInPrivateBrowsing() {
+    return privateBrowsing.privateBrowsingEnabled;
+  };
+} else {
+  isInPrivateBrowsing = function() { return false; };
+}
+
+function getChromeWindow(domWindow) {
+  var containingBrowser = domWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                   .getInterface(Ci.nsIWebNavigation)
+                                   .QueryInterface(Ci.nsIDocShell)
+                                   .chromeEventHandler;
+  return containingBrowser.ownerDocument.defaultView;
 }
 
 function getBoolPref(pref, def) {
   try {
     return Services.prefs.getBoolPref(pref);
   } catch (ex) {
     return def;
   }
@@ -268,25 +274,25 @@ ChromeActions.prototype = {
                                            aOffset, aCount);
         }
       };
 
       channel.asyncOpen(listener, null);
     });
   },
   setDatabase: function(data) {
-    if (inPrivateBrowsing)
+    if (isInPrivateBrowsing())
       return;
     // Protect against something sending tons of data to setDatabase.
     if (data.length > MAX_DATABASE_LENGTH)
       return;
     setStringPref(PREF_PREFIX + '.database', data);
   },
   getDatabase: function() {
-    if (inPrivateBrowsing)
+    if (isInPrivateBrowsing())
       return '{}';
     return getStringPref(PREF_PREFIX + '.database', '{}');
   },
   getLocale: function() {
     return getStringPref('general.useragent.locale', 'en-US');
   },
   getLoadingType: function() {
     return this.dataListener ? 'passive' : 'active';
@@ -331,18 +337,21 @@ ChromeActions.prototype = {
     } catch (e) {
       log('Unable to retrive localized strings: ' + e);
       return 'null';
     }
   },
   pdfBugEnabled: function() {
     return getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false);
   },
-  searchEnabled: function() {
-    return getBoolPref(PREF_PREFIX + '.searchEnabled', false);
+  supportsIntegratedFind: function() {
+    // Integrated find is only supported when we're not in a frame and when the
+    // new find events code exists.
+    return this.domWindow.frameElement === null &&
+           'updateControlState' in getChromeWindow(this.domWindow).gFindBar;
   },
   fallback: function(url, sendResponse) {
     var self = this;
     var domWindow = this.domWindow;
     var strings = getLocalizedStrings('chrome.properties');
     var message = getLocalizedString(strings, 'unsupported_feature');
 
     var notificationBox = null;
@@ -386,16 +395,30 @@ ChromeActions.prototype = {
       // added in the future we still only care about removed at the moment.
       if (eventType !== 'removed')
         return;
       // Don't send a response again if we already responded when the button was
       // clicked.
       if (!sentResponse)
         sendResponse(false);
     });
+  },
+  updateFindControlState: function(data) {
+    if (!this.supportsIntegratedFind())
+      return;
+    // Verify what we're sending to the findbar.
+    var result = data.result;
+    var findPrevious = data.findPrevious;
+    var findPreviousType = typeof findPrevious;
+    if ((typeof result !== 'number' || result < 0 || result > 3) ||
+        (findPreviousType !== 'undefined' && findPreviousType !== 'boolean')) {
+      return;
+    }
+    getChromeWindow(this.domWindow).gFindBar
+                                   .updateControlState(result, findPrevious);
   }
 };
 
 // Event listener to trigger chrome privedged code.
 function RequestListener(actions) {
   this.actions = actions;
 }
 // Receive an event and synchronously or asynchronously responds.
@@ -426,16 +449,67 @@ RequestListener.prototype.receive = func
         listener.initEvent('pdf.js.response', true, false);
         return message.dispatchEvent(listener);
       }
     }
     actions[action].call(this.actions, data, response);
   }
 };
 
+// Forwards events from the eventElement to the contentWindow only if the
+// content window matches the currently selected browser window.
+function FindEventManager(eventElement, contentWindow, chromeWindow) {
+  this.types = ['find',
+                'findagain',
+                'findhighlightallchange',
+                'findcasesensitivitychange'];
+  this.chromeWindow = chromeWindow;
+  this.contentWindow = contentWindow;
+  this.eventElement = eventElement;
+}
+
+FindEventManager.prototype.bind = function() {
+  var unload = function(e) {
+    this.unbind();
+    this.contentWindow.removeEventListener(e.type, unload);
+  }.bind(this);
+  this.contentWindow.addEventListener('unload', unload);
+
+  for (var i = 0; i < this.types.length; i++) {
+    var type = this.types[i];
+    this.eventElement.addEventListener(type, this, true);
+  }
+};
+
+FindEventManager.prototype.handleEvent = function(e) {
+  var chromeWindow = this.chromeWindow;
+  var contentWindow = this.contentWindow;
+  // Only forward the events if they are for our dom window.
+  if (chromeWindow.gBrowser.selectedBrowser.contentWindow === contentWindow) {
+    var detail = e.detail;
+    detail.__exposedProps__ = {
+      query: 'r',
+      caseSensitive: 'r',
+      highlightAll: 'r',
+      findPrevious: 'r'
+    };
+    var forward = contentWindow.document.createEvent('CustomEvent');
+    forward.initCustomEvent(e.type, true, true, detail);
+    contentWindow.dispatchEvent(forward);
+    e.preventDefault();
+  }
+};
+
+FindEventManager.prototype.unbind = function() {
+  for (var i = 0; i < this.types.length; i++) {
+    var type = this.types[i];
+    this.eventElement.removeEventListener(type, this, true);
+  }
+};
+
 function PdfStreamConverter() {
 }
 
 PdfStreamConverter.prototype = {
 
   // properties required for XPCOM registration:
   classID: Components.ID('{d0c5195d-e798-49d4-b1d3-9324328b2291}'),
   classDescription: 'pdf.js Component',
@@ -538,31 +612,39 @@ PdfStreamConverter.prototype = {
         var domWindow = getDOMWindow(channel);
         // Double check the url is still the correct one.
         if (domWindow.document.documentURIObject.equals(aRequest.URI)) {
           let actions = new ChromeActions(domWindow, dataListener);
           let requestListener = new RequestListener(actions);
           domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
             requestListener.receive(event);
           }, false, true);
+          if (actions.supportsIntegratedFind()) {
+            var chromeWindow = getChromeWindow(domWindow);
+            var findEventManager = new FindEventManager(chromeWindow.gFindBar,
+                                                        domWindow,
+                                                        chromeWindow);
+            findEventManager.bind();
+          }
         }
         listener.onStopRequest.apply(listener, arguments);
       }
     };
 
     // Keep the URL the same so the browser sees it as the same.
     channel.originalURI = aRequest.URI;
     channel.asyncOpen(proxy, aContext);
     if (useFetchByChrome) {
       // We can use resource principal when data is fetched by the chrome
       // e.g. useful for NoScript
       var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1']
                             .getService(Ci.nsIScriptSecurityManager);
       var uri = ioService.newURI(PDF_VIEWER_WEB_PAGE, null, null);
-      // FF16 and below had getCodebasePrincipal (bug 774585)
+      // FF16 and below had getCodebasePrincipal, it was replaced by
+      // getNoAppCodebasePrincipal (bug 758258).
       var resourcePrincipal = 'getNoAppCodebasePrincipal' in securityManager ?
                               securityManager.getNoAppCodebasePrincipal(uri) :
                               securityManager.getCodebasePrincipal(uri);
       channel.owner = resourcePrincipal;
     }
   },
 
   // nsIRequestObserver::onStopRequest
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -18,20 +18,22 @@ var EXPORTED_SYMBOLS = ["PdfJs"];
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cm = Components.manager;
 const Cu = Components.utils;
 
 const PREF_PREFIX = 'pdfjs';
 const PREF_DISABLED = PREF_PREFIX + '.disabled';
-const PREF_FIRST_RUN = PREF_PREFIX + '.firstRun';
+const PREF_MIGRATION_VERSION = PREF_PREFIX + '.migrationVersion';
 const PREF_PREVIOUS_ACTION = PREF_PREFIX + '.previousHandler.preferredAction';
 const PREF_PREVIOUS_ASK = PREF_PREFIX + '.previousHandler.alwaysAskBeforeHandling';
+const PREF_DISABLED_PLUGIN_TYPES = 'plugin.disable_full_page_plugin_for_types';
 const TOPIC_PDFJS_HANDLER_CHANGED = 'pdfjs:handlerChanged';
+const PDF_CONTENT_TYPE = 'application/pdf';
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://pdf.js.components/PdfStreamConverter.js');
 
 let Svc = {};
 XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
                                    '@mozilla.org/mime;1',
@@ -40,16 +42,24 @@ XPCOMUtils.defineLazyServiceGetter(Svc, 
 function getBoolPref(aPref, aDefaultValue) {
   try {
     return Services.prefs.getBoolPref(aPref);
   } catch (ex) {
     return aDefaultValue;
   }
 }
 
+function getIntPref(aPref, aDefaultValue) {
+  try {
+    return Services.prefs.getIntPref(aPref);
+  } catch (ex) {
+    return aDefaultValue;
+  }
+}
+
 // Register/unregister a constructor as a component.
 let Factory = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]),
   _targetConstructor: null,
 
   register: function register(targetConstructor) {
     this._targetConstructor = targetConstructor;
     var proto = targetConstructor.prototype;
@@ -79,47 +89,80 @@ let Factory = {
   }
 };
 
 let PdfJs = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
   _registered: false,
 
   init: function init() {
-    // On first run make pdf.js the default handler.
-    if (!getBoolPref(PREF_DISABLED, true) && getBoolPref(PREF_FIRST_RUN, false)) {
-      Services.prefs.setBoolPref(PREF_FIRST_RUN, false);
-
-      let handlerInfo = Svc.mime.getFromTypeAndExtension('application/pdf', 'pdf');
-      // Store the previous settings of preferredAction and
-      // alwaysAskBeforeHandling in case we need to revert them in a hotfix that
-      // would turn pdf.js off.
-      Services.prefs.setIntPref(PREF_PREVIOUS_ACTION, handlerInfo.preferredAction);
-      Services.prefs.setBoolPref(PREF_PREVIOUS_ASK, handlerInfo.alwaysAskBeforeHandling);
-
-      let handlerService = Cc['@mozilla.org/uriloader/handler-service;1'].
-                           getService(Ci.nsIHandlerService);
-
-      // Change and save mime handler settings.
-      handlerInfo.alwaysAskBeforeHandling = false;
-      handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally;
-      handlerService.store(handlerInfo);
+    if (!getBoolPref(PREF_DISABLED, true)) {
+      this._migrate();
     }
 
     if (this.enabled)
       this._ensureRegistered();
     else
       this._ensureUnregistered();
 
     // Listen for when pdf.js is completely disabled or a different pdf handler
     // is chosen.
     Services.prefs.addObserver(PREF_DISABLED, this, false);
     Services.obs.addObserver(this, TOPIC_PDFJS_HANDLER_CHANGED, false);
   },
 
+  _migrate: function migrate() {
+    const VERSION = 1;
+    var currentVersion = getIntPref(PREF_MIGRATION_VERSION, 0);
+    if (currentVersion >= VERSION) {
+      return;
+    }
+    // Make pdf.js the default pdf viewer on the first migration.
+    if (currentVersion < 2) {
+      this._becomeHandler();
+    }
+    Services.prefs.setIntPref(PREF_MIGRATION_VERSION, VERSION);
+  },
+
+  _becomeHandler: function _becomeHandler() {
+    let handlerInfo = Svc.mime.getFromTypeAndExtension(PDF_CONTENT_TYPE, 'pdf');
+    let prefs = Services.prefs;
+    if (handlerInfo.preferredAction !== Ci.nsIHandlerInfo.handleInternally &&
+        handlerInfo.preferredAction !== false) {
+      // Store the previous settings of preferredAction and
+      // alwaysAskBeforeHandling in case we need to revert them in a hotfix that
+      // would turn pdf.js off.
+      prefs.setIntPref(PREF_PREVIOUS_ACTION, handlerInfo.preferredAction);
+      prefs.setBoolPref(PREF_PREVIOUS_ASK, handlerInfo.alwaysAskBeforeHandling);
+    }
+
+    let handlerService = Cc['@mozilla.org/uriloader/handler-service;1'].
+                         getService(Ci.nsIHandlerService);
+
+    // Change and save mime handler settings.
+    handlerInfo.alwaysAskBeforeHandling = false;
+    handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally;
+    handlerService.store(handlerInfo);
+
+    // Also disable any plugins for pdfs.
+    var stringTypes = '';
+    var types = [];
+    if (prefs.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES)) {
+      stringTypes = prefs.getCharPref(PREF_DISABLED_PLUGIN_TYPES);
+    }
+    if (stringTypes !== '') {
+      types = stringTypes.split(',');
+    }
+
+    if (types.indexOf(PDF_CONTENT_TYPE) === -1) {
+      types.push(PDF_CONTENT_TYPE);
+    }
+    prefs.setCharPref(PREF_DISABLED_PLUGIN_TYPES, types.join(','));
+  },
+
   // nsIObserver
   observe: function observe(aSubject, aTopic, aData) {
     if (this.enabled)
       this._ensureRegistered();
     else
       this._ensureUnregistered();
   },
   
new file mode 100644
index 0000000000000000000000000000000000000000..08a2c25327d7df3c77186cc3048bf02b7e72f393
GIT binary patch
literal 371
zc%17D@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6n3BBRT^Rni_n+Ah<Z%{wL>4nJ
za0`PlBg3pY5<o%r5>H=O_B&jxVj>1dW-W_hU|?kNba4#PIA1#9tk+=&k=FgQ+pdT`
zUbe$HMe!GtU6WGBI$aLYZm~5FlsUCLM9rJ>x)ki1v&;2jM441q`dLYetDQYE-*|TQ
z^E<_9?^Y%rUfkJzY)ZIaD_<MC&cmBh$CtgYp8dL|p1-2*N5P)WO~<yom=<t<b<MX{
zV0F4SV~dnB5HBwOxorLKx2`PB3<(U3Mv^-!Sehqq>wl!)(o;G8S#tSeKda!GbIv?G
zbmhsbOZ!$BA5pw%wO?3d{?EV_k<*RN+E-q_-@A4@_jI3mOCvtN%FsWiAoSU(>6p5s
zu;<LjPZQOdDxY?~D4*1|{qN@6tRl%q{~C{f`Y<K**{Z}4_g5KqIsN)|hXdZr_a9ya
P3?c?kS3j3^P6<r_kMx(q
new file mode 100644
index 0000000000000000000000000000000000000000..beef8ccea48c932571c9c9009df916a61c850a1c
GIT binary patch
literal 381
zc$@)!0fPRCP)<h;3K|Lk000e1NJLTq000mG000mO1ONa4wfZ;e00002VoOIv0RM-N
z%)bBt010qNS#tmY3ljhU3ljkVnw%H_000McNliru+zJI29sn4uetG}^0S-w-K~yNu
zb&)+w13?gmpWQt!_hI5eD#4IKO<|GNA0TLBqfH`0&_WQwe<O&XAP9<DC}<P>Cn>#B
z0z$y#_7d0P?!t27TP(BlKC{g1C~Qf5JYZ(0Y`HA=4{6~34>Y$nUyp4>0?NXx%y!n^
zPO8lU5v<pjhGYCX!!bT>vjOn@v3E&~2En%3M*;>A02l=3;j?=|jFv2*(IokqkYu+`
z?f#Sot_0GOokeN4@_t0%AeiK{n7pfbJT>4jCFQRS;-#{C!?(j16Xd+w6vSiLIVVO7
zT0|%UMkoT>Cd44_!h@@9OoxH>aKs|3a2xe<yZqHZjL=WD-gRM@<<aV&v0^Vvl-BK`
bNbKrg3mRK+IzR8P00000NkvXXu0mjfaG9N{
new file mode 100644
index 0000000000000000000000000000000000000000..beef8ccea48c932571c9c9009df916a61c850a1c
GIT binary patch
literal 381
zc$@)!0fPRCP)<h;3K|Lk000e1NJLTq000mG000mO1ONa4wfZ;e00002VoOIv0RM-N
z%)bBt010qNS#tmY3ljhU3ljkVnw%H_000McNliru+zJI29sn4uetG}^0S-w-K~yNu
zb&)+w13?gmpWQt!_hI5eD#4IKO<|GNA0TLBqfH`0&_WQwe<O&XAP9<DC}<P>Cn>#B
z0z$y#_7d0P?!t27TP(BlKC{g1C~Qf5JYZ(0Y`HA=4{6~34>Y$nUyp4>0?NXx%y!n^
zPO8lU5v<pjhGYCX!!bT>vjOn@v3E&~2En%3M*;>A02l=3;j?=|jFv2*(IokqkYu+`
z?f#Sot_0GOokeN4@_t0%AeiK{n7pfbJT>4jCFQRS;-#{C!?(j16Xd+w6vSiLIVVO7
zT0|%UMkoT>Cd44_!h@@9OoxH>aKs|3a2xe<yZqHZjL=WD-gRM@<<aV&v0^Vvl-BK`
bNbKrg3mRK+IzR8P00000NkvXXu0mjfaG9N{
new file mode 100644
index 0000000000000000000000000000000000000000..08a2c25327d7df3c77186cc3048bf02b7e72f393
GIT binary patch
literal 371
zc%17D@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6n3BBRT^Rni_n+Ah<Z%{wL>4nJ
za0`PlBg3pY5<o%r5>H=O_B&jxVj>1dW-W_hU|?kNba4#PIA1#9tk+=&k=FgQ+pdT`
zUbe$HMe!GtU6WGBI$aLYZm~5FlsUCLM9rJ>x)ki1v&;2jM441q`dLYetDQYE-*|TQ
z^E<_9?^Y%rUfkJzY)ZIaD_<MC&cmBh$CtgYp8dL|p1-2*N5P)WO~<yom=<t<b<MX{
zV0F4SV~dnB5HBwOxorLKx2`PB3<(U3Mv^-!Sehqq>wl!)(o;G8S#tSeKda!GbIv?G
zbmhsbOZ!$BA5pw%wO?3d{?EV_k<*RN+E-q_-@A4@_jI3mOCvtN%FsWiAoSU(>6p5s
zu;<LjPZQOdDxY?~D4*1|{qN@6tRl%q{~C{f`Y<K**{Z}4_g5KqIsN)|hXdZr_a9ya
P3?c?kS3j3^P6<r_kMx(q
new file mode 100644
index 0000000000000000000000000000000000000000..1b2df8093d630ca5e4b7c9127fd4fc855655599b
GIT binary patch
literal 9025
zc${^ac{G%N`^RUDeczJoO@%B&_UzRp$(Cj!yOMoN_FW<p5|TYx=N7W>OxY5WG<MBM
zl6{LI+war!e1GTk$Mc<Y#(mB(Gu-#<dS9>ih%wZ^!bHzQ4}-y&u4<!=z>yW)*=VW3
z*DvJ~9pFG~a|fdfj*Q?A(`}Jt0|y?v|2_u$|0lry|7MH$35Q#})S`jGC~~i&FJP47
zf}g#;cE_HxGjxQE-t3d6)0#PL^f`Yt?3=Boh|HaZQ8C$4={$9+vj+^1FCHH*{iH`_
zY<KExJ^Rwd^wOzX@p*lQ=SX2m3$)`!`((}B5A(O#xx1=;z1so@eCy{X8S3(u<ANx5
zs69+cZsG7(cw_utoUg`H*YC?r?>a)+C8VSR@zINoIf^>`cn!Rs5}!UIF(oDC`4Boh
zI$v8j$sfMY%}A}GU0`HIO%a}RG51rjb%>|uMn%5%opf4j)TbYfEA}HJBUfejwtxQo
z*+)JgMny$sOG-#2_s(wk78uPtwpf;zm6snm*)dB`E9sB)_xF>vE0>p-MJy~VtY9iV
zwi}C{4XQptL7B;%PdSS*Vlg@e8=uGrXYs4c%fXFxb&9AS*i+7wWD7I1Ky?j`HcuZP
zAF^xQvlSN?mp&IaH~p(0bRH|{^`(v7k&RtmS*a}=nin*}eJQD<>sxa2@K~F>m}^)!
zl1M4@5s$~`nU-=<H+v}Ysj8_R6x=L1AbfC?b#7{I4qDycZ|48Wed5IWQ#pzGnEdr1
z^&=skIA-|pW1&Rt(;K_{h^sSk8twg-G}81z<5N=y6dq=59W(1!@iYPyZwqOL$HsPP
z{p<q*ev@xk_<i%Yj2apF3HJ>P8S%cqx3?E6At{*>78pMhL|c8T2mBH0D6UwJXEJ$t
zdBI^hy23PJd-yA3F13PdcPdOwjee}HdAv3h?UO*El>3iGeyx8qpPij;zZIZ1{B$cO
zKXBbIi17ttMdz_Lz5E$#JE{KHjg5`tEM6|It}Pp?s{>kX%iq7}`*?U5(i~=IWd)D)
zL3Z04&#xLA8><~ZethWJZ`h9?Kf3NWOfp(O`wbBmFgVQPTABL0ayM1?Ltli#HBP__
z#F;zx{z4l5zme`5dOZY?uC%kgfB}zD{DZYGYqv%cIXk`XSRKz)hpK#(TMG4;$2UiW
zr-ogY>?Av_k3}9F4`2`v6jvLq5=fTt8;f|UU^z9Et@cZZ^ZT(_QzZAf-tmW;O~d*u
zT2JM-84#NpX+-JGEUC$dbLmH%JJT(rrJXQ1={zgc+!Qt-7Q(c~&fQS+c5*V4YDaHq
zzl=S=xFS@tH-KK1=fsI~C3>DL4u)7O39eX#)WYJTV0BH+!RY8{{ST#&!K4IDp|vFF
zH;iEoew#v|w^AS{Cnr=CSx(;Gj<>hB9{^`pE2+m8l{3)hZ9$%JQ7|(xHT``H%}C0%
z;r2zLP?ot+Wo_-Y|D{Xqa%->a>d1TG%G!*0t6&KwkR>?!`uMzxK_jVjYpbh+S{fU9
zu-J2#(bz5W{QP|AkGVObfPld77R%ML8Kq8Vae-o|lL85$=M)sSeU-CN@JGm30xz$i
zS7M`Enwwp!>*|g^fBAy>t`@C*mOW-ay~p;0XN>eG`(I@8mH31NXZR^jdct?lh6r<%
zG1OHZ?f1Cz+m+p*uM|!+)YLARnk(2g4B_YR-@irp`T4WLFwU&>kr!iQV{;&<>W!i~
zF?%?h^}D&vU<9VcTbg^H{>67Ab3uH;)BLhB6up-Sd(4oF!d`~!M6fyZiFyIvE!RKV
z)urWTXJ;o!%N=!&-lQ0M{rdH3MMXtH$cacIy%c=G_^4eF(V`jT;P4(UY-VQGgspK8
z3=C9C>Um<Ir*{HYyfEcVe@#wNQ3I9QTZ$K2Lt_zdtv$H4EzaJ=u3DCcMQ6rnpUl!%
z(OiD_?p;o;YtGG$?5U|KMetg8N#!vyG5Tz0qu-A)J<};L+U)7MdJ%g7!(qgDVmrIw
z*Ri3whJMv;ZK35nZ?V_U-y1k0?9=FB5zMuVUekXuj`-h<i(d{^2549O!#L$1#;<9c
z`Ez!KiYT(^5#{ywiM(=J4$4x_5ralaj=UlfdQFCCkLxx!WvY%hGIbujbnA*_$Q8eQ
zPb3+MvS`3{)H3wTeZ!_-R5>X<K0MT!FO>K3R@Ml4J&Pt&Zs6R}$+f*uPkRMK5JHvW
zT}?ez{msn*_zlek-3LMHt5jvb4-NxszFZ-(DsAirC4}*+sj0ONB|i+i5Al4M^_;@r
zxzks-?zgXar%$lfwL2U;Q7c&DM7u`#5_5cwk|I2VhK6Pfxp6x%aIYOCcM%%7e#70}
z-P_)NZVON$b?w?U0uG0pLwX+bqp%G_VX!xijgz`w$VtEGv5hhxSr+4pErm?Wp{=q-
zEpV0H>uPG$;D?nGi(~KKzb^-*%^YLH5)_-7ng++l##)SUUY)DtHo30-V3&?pQ0sj@
zk&(&C{kabxKF}L-CZ1wh%Zu(nqr1Ah6P}!t;^pCqkASl#>R4G3hvMRxjL{NoF>wBy
z$i$tu-ctcWtTAX57k#8+ff3Ggys&+GZLYn&T^zt<DfDS<>;xJ+f!!*D3PHfNIy*W#
zQhk?u;2_77m6_R$gK#{B2hRO8K`g%>UCezHMwX-KzPAF8&^+!Ta2R|VS5k5fK2kNd
zG!YpYY5dmu4I&zGfkeT&9?&wsySw{&ZEcOqTB7#@OdW#-u&+p=P+zYT;%#NdC>gtM
zS2*LDUe+}=sZcPE*9Kc)RfdO$-Si9$G`M+qDq)6%-fne?2@pBPr8ixL=)PkJO7Zs@
zxM3)qlQvc=lpoIKFNK_~sHmW2eevSO4l5%yad;v7V(yxozkdV${t%H!6hq!+^AEj!
zHASc3UTCOB38V)@AP_h&<pUTi)>SOESHs=aHPQe!P&pnmaa2CXz$*fOyRyY9^%vq)
z{}ST~Oqu}k)_)ME{!fe>BssEng<jel+rSlHD<O_Nw?0XTn(B1B)F~<|8rPXFK^4PV
z`G~1bPhkuZO0f^;>xn{`pJt^!Eu5)}kKv$UiDO4}QPVrh>zqvdA{{uIz{FdU)ZEjz
z*QaZj?N4$}x^c))GJXkN6u`hjU@Hi9k!^%`ycN?vm0WPu+h4z`d8Jz{-TQWx)%{Va
z;!Kl3MeN1(z9BwBkh_CJF`XYfEoB*qqoTODxKrNV-f?(hqV{)R>=wLGHmyEfETMa|
zcX04}O?30*__(qH<BYBnU+dK1;602er~2#i@;}KPa)OsP@GR0eTq8@kEd~Ne>hyxd
zQp_2!T3J~5oR*em-e8V0d0s>Frl~1#mPEqq7Y*US8|t@9e`RE0X?r8Fm}Z8?V2R61
zOXgYW>Er#go~h{XHLeqTMK^;=pw&}}@24E(e!Bn``TfL^-B|np^0N?9Mp-$)Wn#Nz
ze0+SU69$r8AVI^*!eT30TNf=rF;(WLdU!)Ur<__f*RWVPqrbIx*0VrI7mTUZD3N%S
zXO60aNy^L1ZxuULK|aM!6H7}jH-dwMUmV5GYsl(VH350bgIx9XHtFnO8Wd{kIjiSL
zWnYB28GR^%@p>e_eeu(PYgbLVG+i#RfUGhKPaw)vij-L%PP+N-O-`D-SzB9kys<!y
z1miWacRf8T18r=kv!O2bn1@uq<mKed5pFORQ!}$mnpQ%r(U236kLBTx4s>H%8<8pu
zvh?D#gI#SS<|wPGGQ#LciO~34>ou;Ya8xm5fR)OA9TpwLnfUGfdu`gaCkY9mGI~`c
z5_ZgG`(R^Z<K$XHQIUuQ@-o}*&atZnMm=qHb*UIEgHTdWbxmDe5s35>6A2$zH#bWh
z+T-$l{mK27%upEhCWQcIMmy#&-f8|N(ocNF0p8#K!#geDJ;_Sj%$KuUqx%PHUD$c(
z46Y&lc1dCxq5h25{c7bU>HHF2hgcJ~jus~BI}cpS)uRm%>#pSs?H$HcR>!#!#2?)E
zB6A7Xo<-ToPhB4EOssnJ_4-8NfqGUbQLyd)M>4+T>5XkHsq0$B@lU^=9Wk%bv2O2I
zX?9c{;DbpYoCQs8-CBp+iE;)EFc4aJm?AGKhV{<O%xLB1pu=B6S|TDM<#4jHii*?^
z&jvFbW+B#ACJolwZ!42i;>1Y{a}C=G69%W-$D-~QI%I-KEa#1yHYgfW1Yv(v<J#2P
z)U>z$<42*po7+dOL@tsuiEk~d82Y}syU4-85$@R#WaHqVJu*DJl$4azz3$gcVOT;y
z)tB)O%z6s0EG;#=r~jDl>+36zvT@l#19f?W5wkG0v<$JG*!E}hx4tz@EHYI!B#3}z
zG>;KW0W1f@R5=p^IG(a6GP5Ou0ByW`D-?}P9I}O>JLXqb4ju2@$-9h>y8NA&)GM2&
zTUca{QbZi0k@sp1Mft{Sm*vvEa2R^!`1>Q?Q`{cv>@l>^cnK5%jkmS7hLpCn9LSWj
z=}U9>>FMbOL(qXh5&0++?u9rt=<rJxKN2PtWwTGaPw`~k+(lc6wVrCS*RD!&b2li}
zF@40pP>I8j_a|kmmAQG}>7<_WFk-l1Sl!2BsJgbcRR#0|FB_Y;f3m^T!{h0~&!5G7
z{QNaA3ov0Rw=3RnZ*O0%Fxi*oV|J&Z2)FC4458M<wr9U%6ylBTbQ2U5tfLTw`C<r8
z&dz(Z+*I!%9xOrHuTBUl&xZT>@uM*8==0~#H-_W{19uBS#DlXwd~R(3EYCA+xX0Sd
zDQC~ebCh7jZeimPPvYaZSr{1^5wD<iry6A-e<PnAE-_7))|2(*kKl8^Xjn*xBRqd0
zPxmho@3gx0SH#o*LEb>y+!$C)ck{@-E)DO6q_jER!6(TbFLc)i#O30@(CNE;&i`V<
zQOuox?Rc5F?NC-d-&0Lm+6PF^$p#6pT*G<c60Rs_s^z}0@`$14)9q%o-{ybRoVNSS
z!aTVdOg3LpBQ~s|zQ=iMc+BvTxzaz*3c?k`<AvN=lwce?d_RL%;Tahj)U1x6xCj{9
zUjU1`h0%n8fk7)T!$dO{GOenrG7g(>ZEZ~e{8ybib&3cw*9?E)qM!mnTx)Pvnhsbx
zsHP;Kd?x4+HC0trSv^@fB_$<gb@h;iz1CY|DQ-(kOK4Dgj#v{FNaF3%%K8QdIj^9~
zrl!TwS~Y5216|$8a)ML*Vq*)$bCd99&R?x;F$7q3E0q8+o8Z)eId>eyjeG@}sw*om
zOpT82lo;XAm-8Jawigw%O^gV~<7e{uy3hXD+&x7yU>g)BrAOzZ(a%#;2V5o&TdJxy
z<>!QuTjAm1b3j)GAHRPStqK#c1YXd%(CEfZdnT!88}!sNjM$b|mb$t+b6~db5c$C8
zrCOpkcd?Txwi_#7)zT7NP*gOBTs@Kb{Q2`qaO21JQ_9bW%5!kTT5Ba1QJ>yB(>-c0
z!4Kfcnj0Fq4GrfRbmu%923uNM43LTMX+7woDMN^e?9$TG0?<hX;TqI~K^L$EAQ(3G
z_xDRc`Tq?DJQnDhWQq<}Z6Fq2ce}!bKtAXOKdzC|I~xI(k{L45;|A`_eHf<Z)C*~j
z_h9b^Pw#~&lK$|n0M^YKgEbB+fcRPWso4C_B5Mi?3Qn_{f;2c5gN{eE;0#6gTUuKW
z5!U%b^8@I7VWbps-Uw75=w``zz`L%V-rcUQu2xDkcBb?8`u5wm7hWe`Vq!DNJZw`t
zg4w0eZPmgGHvi(C;a?(tUzHE=j{k>u#y?79pq=5%-K~*uF79ge&yA>UZ`}BJ@gkRs
zeAa8z^y;gU)t_!J^u(&X*UuKN%-}??85(l?ts8bG30*X}qoO;|lzj10Bs(vnRc~)t
zj!w*nVE?Gl+I3t?buF-UVAbF6FlhSz`$V!Ye)i6!NU_q7P=q<c>DplrI}M4lj6%;!
z;?0{khID>VNr}|+;`V2b78Vw~)=!>1QTpJ08)k`>r>3Eig2e!Hl?6F7=%Ex;UQ*Hv
z`<*xFkO1)%xG`#qh0~|@5-Qr-R=E;IczJmr0U03-4i0j}pueszEh!Dn|C$AtaeRG!
z-J9(m%$T3~5t&3H8T`lKrP2!hskA$8SA<N|O5J)+$cNmp-T)9S5MMf{+0O~taCemg
zUyp`#3Zjc4H#b*T`Tl`{wmw@KZ)Bxgy502qXh-ano1<e)LR{Q|qP+ZFgcKSr9`-ak
zlY=eo#S0}lz0I_NIdKn0q!A-^L;{$(%V=G;d3c!$*ju4*Bzl?ZDZ=qRwlWQAJ+_Z$
z6<X@*=K5wmvp{1&)JMD}(P3&Vu&1E$rs`_1+=_~I2`g58J-z+`Ao%C7%aXIzp-;|-
zuZA~8*-TQ4^?r{XSv+8Rfe4|}e@k=nDi({C`oxvcZ6zfoRUW9CEf^jm1k0p)g^vIo
zyAAY&QzYb?ipRTL$OtqG1v$CooXkuKW=2MrqM`YxHr%T|zP_t4Meqx@MP8gvt*NQ$
z2Lst$Q{xTlwCmo%VG*p@X_+mu^C09%Z=$x1>JTOXLG$Z=UE!f&n~)RuW;YvaVjc2!
zLVP?mY^1MG_v_qdi`cPbTbW%uH@6ij{KCqLx(Kp1y7|E6TIcP-oPN6*D1hf>Ztire
zV>*XsB$WNpu4)}4<_%3oet6~eZM*znS1@Xp$@9XJuJm8TGyO}#E9lz+;=BJLp7~G0
z>uH<a;_MFX{w^;ZLdrl7883?-vv(<t7mU$%GLsBn`Y_se`|&k<oY$#(F=YL!rw29V
znR-@g<8KoAqiI<g&$+5CijydVOs&|>*t|Yv-s<CZaLp7nub&GYXvhrw*sykHqvueV
zwdrNB))>{EI0<ezGfpU@wuX_z3$IDN$jKp7gafHwHOKu#$?H`eqXp{huXC53LJT7>
zr>Yvnpsk>&Xk~DkJp!ovqwMT#)-?wYkF|IxYSQImm|wU|&cKLwK)}NB#1R!0mCNnY
zL11Wnsi~<y#wR4ic`5UonVLdrnVCT%NC8r7_Y6)+L*s}an*uIh@!ea#;kK#*q2HDC
zztk=ZW4B76HbYUl@!F{jUEvwc%Ca(4#hW*u%As$6>T2K0irWx~RZE2o`ODw4?%opA
z@V{+}-2^sa34nP7X6h74ifvHlr3Kn8I$GDj;J9RO052o8h9{iuJlOPsva+)GC5}81
zKYw}o`VwnhCs=lPcz7U11qB)4wZ)hM1AYDG2J<r6)ZP<eY%yqxVy8zCbFtIWw{PD{
zLGgC4xAk~8?Xd?Wx((WHI>&#MbwZPOt;L4B4R*-Bemng~0-kb>RT{1umYkYO1m~ma
z>%_z!8FYBTl9G};czJ80_x|wWmb!j{Q7Guh`@_RGn{2q3L5>y}W7D#-Bo+0BPVn&j
zW@UaP9_9yxeKR5=q8r~20yk%9-fIyUh?aT|(ZbTwk~lfZSXy45gLnzauRO#eoWnJ(
zKi|s?Ae~*yfK0)#X~5ektV%$6b;chsAU#Eq`Vw4=l*~nig_e{NZ^`&$q(5a!Jw>O5
zVxz+3^;I+<+xh)hI-sa~r|AmkLdI}YQ`2md=Ua?x#S4UkgM*#zZJ&C~Bb{%x^)p97
zhwx?j6J_pIK3#u-&+;!(|1v89fZz2G_^f}T{=y%%iE5K|&rUj5IM{kT=@I2!VL3gQ
zLM$==S-#dIx(Ly;w)7VtKInMBJFlhlvny_Kr8Ck2{~G-{4t_j6PUxF3(=#EZa}V6V
zq$;K1juiwnzNf{dZAUPW_vbv7$Qm^2m(?4s;BJ|p4lk3?`{$p+#A4U&FEimO-4NbK
zx2%JLRB4lqF_<8W<oL%|N+76}a_R6eBo8B^;9-hgURhZw(NyM%v(zmxaxGgt{Pg9E
zErJZ%pc@2PEOk+zrlkZ<&<>tqg3h*=Njo(pcZrFK>CXpVM_;F=<bgA~fnAAZW@c_>
zXG7Gxa(B&xoGdDa`uh7j`MY<SwMCM80$W$R)dos#5Z*vhzCJ!qY6DH#`b9~ajg5`U
z^5@RoyM{d@iUf69K|x^wKv223xQIn=e7Kjnf5poD2EiBffP#jG-2*&c6YeL8<bjpN
zXrs}#4h}!x-^)yWes}hKcr@q2;^GmvfWX%j7Ic{V6Yw(Sssjv&lM}UBV4IYhqvldQ
zP0<v)L5D_Qw8Eb*HqM3@6r8cbvS7B#(a4I~y~CQM88y3*kV6*(1B07bGG@#A-o1O9
zO1{*7w6?lKnF#$9F~b`-l3<_l-Y>tID29pPb>w&s9Bpk+b1_m2Qjp;c#nh<Ol$5EQ
z?CjP_0)apq<j&n?ftSOiEH5u_0DtVx%gYnb%F22hq<&O&_tpR~_%fI;&>{vRxh*R#
zZOqcdM9IP3o#<;p#whVw!ZoqtvAjOi+=_aeiK~0^cmzoi>+Lep%CJw7X^b_)il0}w
zhW12$^Y!uyf|~%xYBPIY)A=Zc2_+@fmX(=lPICw<f6U`(DP)6zt{5l%oJ^Xo4ps^V
z7O<eRvvVjF1r%U&|GsQPV`D&T^4l}l2``|o!_|I`9X_^FpNZ|Gzqn`n-`scSisb<A
zoBrXR{lDC2*mHM>8ngy=Nm(giWQMF&WLw|(Tr!Wa(lPCqJ9SF?t&VIOsX*FRWDCh?
zknMZS_EAMFjTE!0X#KTEMbh+YUNkne%DvKbdWBbabogrqnD-tI7w)w)tZs%9$pf4F
zM^ep9{;o%K9&_wuYKqha0TRQ@-f|-OJdA^zihY&S#LSF=Vp{w3Mw&s(Mk?5<MA)nQ
z_wQ>p>Awmqg$C2o(hS01%_xn~M_&BsJyjkR74-^#0IJ_>=z3d*sL%x=R{BUIdwcum
z(fc8OemfuJ`VGZWcmzNfoA=N5_ww?xzkk2V+24Pch3#C}^>*o{&CN|KL+seplwSAD
zx`bih+$Pz%#<bBKC4wOsVHdxD&u=qF5yiM-{a=nwOqiKiSqWap(mjrb7KT8<?%3aN
zURhmLiPM_Nol%M#(NZ-cv;mQt1C6wzzJ8SAkduvShk1u?XRz{h3J!-G^u1hLQ?q3w
zlh({+8#aO0JH-_n7m$4%{-xHn%CuD7t;RI8hJ!UxvuaHDrr0qK+J-V-Ei4VzTNir;
zsC$&_1j;;~Oc4XnudIA#L|{AWCv}bm;_LA3@$vCF;f5;V09ZalN;h~SgE}mTCHeQ>
z-f?4NWB2sJU08i@s757?Hg&{X7aT?hRGFf%z$hDCSYa+y>|`LE<X;VnvBT}#-*W7#
zcD*II0>Alxl^0r8`ZyHOa<~r4XO$6-lTT1k<Gw}RQ041jxFG>0B#ImeFZlRTe_`u4
zORZ}Y9*doN(mgXGo}#0;RsyBtud$tH+F8kdtnKRKvn|GIYGSe@$;Yg**)0p^XaR_*
z2tKx_rzhc$&KJ3-eQ~N$FTPn~1uYy4b5RKNwYDbl3cZAkooY-ENhG1+kEGhQQv&;K
zDn}Gc1$1(iX5#n$f}P`k!!D+{>jPkK{s(qW0DGc=_J96?s<?VN_*_qMJ}x5dV@CSt
z;m9nULH|yXEROkt7{~G=G8W}DU-+Q@o``6&&sbc^3O$N*QdIkD?x)DuPg%V8MBDQp
z=L_rdWgSPzN^kpYKEx@jc}zFW?X~tc%;E38cb;Pj4`-MRWv%~I2hX993=!NqA136%
z@`k8n?e9NEd2!lf=Ty$`ZYft+*8^B|e_tQB@!4JO`dp|2NP{|ld1<LRvB&mv{7lf`
z&=3bg-JobF0^->ubimF9AG3nzAvdP}d{|sz67;lp771LT+OnL6OeWI|kBpdcf#%8y
z0^XimYZK&Yc`@YT;==f_ecC<zqM6x*JJ5g#uvF{*!}`w7&X4J7o1DeQyfEH`Zdq}0
z@k@|cA<%hs9Kp#R)ah9eh!fB6E|1n8?AIJ4W4CU(yRVAiFblhrljcmL9DsG|9r%tq
zPaF!kH&gT3%KrX--)BAFErh5hsoZ$i{Z!4$M~H|rd3pKBYXzSLv*!+i2{1M~zwA_r
z)ZPm(2k&LFLgS?n8SCv35~2=TAGK6hAoE1+GH8M-kn;DVdG$61I0xR~`Q3w`-ClJ3
z{QQo1s^4L2=LMzp?lbQ+l$0zk0)Hz5o8(E`osRN{j_K`@7f+`3&aS|2wMz?7T)2dF
zba1Fwmyy|U@b=zHTx={#(Ybp%QG0*Z-%`aIoH;>$KECFhfw=_w$Oq>Y6t;o62=`VB
zFoZ$1u)^p}3M(xn+u79KHaSJbHU>1-TP0C@QX)lXB%Fwqr)_3WObT0FiHXJ(6%}!4
zI@s7uUlrvHpl_z2XliP@vK!RO-2CFji~2K2EZg}8TcgsOfh9&bRYQVL7#M=w>FMbK
z*mc6TQ;o8z1Q)UO14(@P%H%<)`ymzDlhS{Iuw?lc?Z^Hl+7DtG0quGJ(9ZQI+WW6z
zY&g3@%YD7lGl=H)eJWo>Ik*ySzh+jSSc(dMA(@%>aF^zRwrk!ev4@({7UwhYcVF@z
zM?DlC*K&Im$$L)sdZD4Ci!jZ}<36TdA{_|=e$$!c%sG#b{>*%lKKNGJYQ(||RoG+Z
zT{tQ35~U~&#NWjI)59}fN?Jyyk|x<Pef!<+?`2gS1U16A5eNj0%KjrELlGp1G=99X
z#YmGx?_;y)TW<;qI$^hKU5Tu0?ekr*<lgyjCk%^{18PA1<%;c8-#Iv>0p2Od)IMrv
zW#yiel~u|oAaM9P`EwYv-jM$4>T26%^q0}mQU1D#19rdFt$;tBu1-=?k`Nnx1+5AO
zWU{ME%UM-mmGYZqIeF#RFE11HA<7sXgk)a;Pp@&^+Xe2~Iep@CzKfSvbzo3X^9}Sx
z<Tvq-+jtJrJ-|#Jh}e>DNjhs#%v_|oT@YmmSNySlx&Ed<m$z6{QnD26vAyKpS()x*
zhC0W<3{xs9D#pU=?%q;Hem;#<ynH%SS2!bBop@JYDN|F3Ra-czAIa{cq!;;uHyRc(
zJ3DK@pB9go)8maslxm)he|Y)KnKLb#qm{3vZrr%Bsw!Y0`-%3a<_VH-gLxuYieAV>
z6RM96={^ji!)Qv25#CpW4gsE?8)nGFokaa2DS0_Lm%CNB1U=M29R-!O3nNyerlGOA
zu&^**C9#;?J#!1CjqxYFa~t{cWpZL_suWQMCtv>l#4&v&T-5L+GAbIwrKNc`<%Z5(
zuygmr0>P3fDqAEnoI;^cZbe2o3z9MLuNEV*yyOr3w~W+t%E!jX8zqr7ut5hoz8G{P
s{0y=NY{mQD-UQ!=e(ZcP8HcHxG}a?9+`P23$RBpEUe-sKU9^7ie*^@)R{#J2
new file mode 100644
index 0000000000000000000000000000000000000000..604e652e5ec48052efa7b342c41c33729dbd287c
GIT binary patch
literal 503
zc$@+E0SNwyP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0005LNkl<Zc-obb
zy-Px27{$$?$UZCyA`$~HWJC>7L`x76v;_%@mM9iB^r1sVf!&a+LRUjYsGy;SXlXDS
zqS4Xa%|ApGMeLk-iHe)y13!4)=bZC?+^cI*oxlhzV3LU5D72Q<MRPnJ-!GL)kJW1R
zov`PO8a=H}9?#`+$!4?p242AnVb2*gdj7h}U?dVbV4z;FKgVLRw8diCHJi-=_MB0p
zhhD7>ti@vS4yLDQG<t>g5*Q=w6NyBM8a-G`S{uxk%jE}{?yXkqCe|j<DHz6ViyA%j
zYHctN@fxOENs>0OHhj|zoKb`4hF+}=rVE9F3R6Z^Rhdr$-2y3-$y`vQ2kR-VKQN;x
z3QcOQR^uk8Hk)mQu*WP-&F}XI;T`=Q9`X5ne*E)Gm=u8uQbhDhGMNm+I|q7xguPy`
zr&6iJU^;I!8e!bzJfF{p@!n6^yWQ?Xco)GC==chIJf1CtWrR-<K6W~tN0_gHRj@8t
z1~xDN^g2;}fk0p#;WLCo2nQVwM@W2O6Lnw|^nxyNvM89c+3YsL`v~vZ?e;_QzJ6i+
tw+vuGmgQZS%eB*Pn57q2od8n1;SVuD34zFH^ql|z002ovPDHLkV1l!1+}HpB
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -39,48 +39,62 @@ select {
   display: none;
 }
 [hidden] {
   display: none !important;
 }
 
 #viewerContainer:-webkit-full-screen {
   top: 0px;
-  border-top: 5px solid transparent;
+  border-top: 2px solid transparent;
   background-color: #404040;
   background-image: url(images/texture.png);
   width: 100%;
   height: 100%;
   overflow: hidden;
+  cursor: none;
 }
 
 #viewerContainer:-moz-full-screen {
   top: 0px;
-  border-top: 5px solid transparent;
+  border-top: 2px solid transparent;
   background-color: #404040;
   background-image: url(images/texture.png);
   width: 100%;
   height: 100%;
   overflow: hidden;
-}
-
-:-webkit-full-screen .page:last-child {
-  margin-bottom: 40px;
+  cursor: none;
 }
 
-:-moz-full-screen .page:last-child {
-  margin-bottom: 40px;
-}
-
-#viewerContainer:full-screen {
+#viewerContainer:fullscreen {
   top: 0px;
+  border-top: 2px solid transparent;
   background-color: #404040;
   background-image: url(images/texture.png);
   width: 100%;
   height: 100%;
+  overflow: hidden;
+  cursor: none;
+}
+
+
+:-webkit-full-screen .page {
+  margin-bottom: 100%;
+}
+
+:-moz-full-screen .page {
+  margin-bottom: 100%;
+}
+
+:fullscreen .page {
+  margin-bottom: 100%;
+}
+
+#viewerContainer.presentationControls {
+  cursor: default;
 }
 
 /* outer/inner center provides horizontal center */
 html[dir='ltr'] .outerCenter {
   float: right;
   position: relative;
   right: 50%;
 }
@@ -199,17 +213,16 @@ html[dir='ltr'] #sidebarContent {
 }
 html[dir='rtl'] #sidebarContent {
   right: 0;
 }
 
 #viewerContainer {
   overflow: auto;
   box-shadow: inset 1px 0 0 hsla(0,0%,100%,.05);
-  padding-top: 30px;
   position: absolute;
   top: 32px;
   right: 0;
   bottom: 0;
   left: 0;
 }
 
 .toolbar {
@@ -240,17 +253,17 @@ html[dir='rtl'] #sidebarContent {
                     linear-gradient(hsla(0,0%,30%,.99), hsla(0,0%,25%,.95));
   box-shadow: inset -2px 0 0 hsla(0,0%,100%,.08),
               inset 0 1px 1px hsla(0,0%,0%,.15),
               inset 0 -1px 0 hsla(0,0%,100%,.05),
               0 1px 0 hsla(0,0%,0%,.15),
               0 1px 1px hsla(0,0%,0%,.1);
 }
 
-#toolbarViewer {
+#toolbarViewer, .findbar {
   position: relative;
   height: 32px;
   background-image: url(images/texture.png),
                     -webkit-linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95));
   background-image: url(images/texture.png),
                     -moz-linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95));
   background-image: url(images/texture.png),
                     -ms-linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95));
@@ -260,16 +273,104 @@ html[dir='rtl'] #sidebarContent {
                     linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95));
   border-left: 1px solid hsla(0,0%,0%,.5);
   box-shadow: inset 1px 0 0 hsla(0,0%,100%,.08),
               inset 0 1px 1px hsla(0,0%,0%,.15),
               inset 0 -1px 0 hsla(0,0%,100%,.05),
               0 1px 0 hsla(0,0%,0%,.15),
               0 1px 1px hsla(0,0%,0%,.1);
 }
+
+.findbar {
+  top: 32px;
+  position: absolute;
+  z-index: 10000;
+  height: 32px;
+
+  min-width: 16px;
+  padding: 0px 6px 0px 6px;
+  margin: 4px 2px 4px 2px;
+  color: hsl(0,0%,85%);
+  font-size: 12px;
+  line-height: 14px;
+  text-align: left;
+  cursor: default;
+}
+
+html[dir='ltr'] .findbar {
+  left: 68px;
+}
+
+html[dir='rtl'] .findbar {
+  right: 68px;
+}
+
+.findbar label {
+  -webkit-user-select:none;
+  -moz-user-select:none;
+}
+
+#findInput[data-status="pending"] {
+  background-image: url(images/loading-small.png);
+  background-repeat: no-repeat;
+  background-position: right;
+}
+
+.doorHanger {
+  border: 1px solid hsla(0,0%,0%,.5);
+  border-radius: 2px;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+}
+.doorHanger:after, .doorHanger:before {
+  bottom: 100%;
+  border: solid transparent;
+  content: " ";
+  height: 0;
+  width: 0;
+  position: absolute;
+  pointer-events: none;
+}
+.doorHanger:after {
+  border-bottom-color: hsla(0,0%,32%,.99);
+  border-width: 8px;
+}
+.doorHanger:before {
+  border-bottom-color: hsla(0,0%,0%,.5);
+  border-width: 9px;
+}
+
+html[dir='ltr'] .doorHanger:after {
+  left: 16px;
+  margin-left: -8px;
+}
+
+html[dir='ltr'] .doorHanger:before {
+  left: 16px;
+  margin-left: -9px;
+}
+
+html[dir='rtl'] .doorHanger:after {
+  right: 16px;
+  margin-right: -8px;
+}
+
+html[dir='rtl'] .doorHanger:before {
+  right: 16px;
+  margin-right: -9px;
+}
+
+#findMsg {
+  font-style: italic;
+  color: #A6B7D0;
+}
+
+.notFound {
+  background-color: rgb(255, 137, 153);
+}
+
 html[dir='ltr'] #toolbarViewerLeft {
   margin-left: -1px;
 }
 html[dir='rtl'] #toolbarViewerRight {
   margin-left: -1px;
 }
 
 
@@ -282,22 +383,24 @@ html[dir='rtl'] #toolbarViewerRight {
 html[dir='ltr'] #toolbarViewerRight,
 html[dir='rtl'] #toolbarViewerLeft {
   position: absolute;
   top: 0;
   right: 0;
 }
 html[dir='ltr'] #toolbarViewerLeft > *,
 html[dir='ltr'] #toolbarViewerMiddle > *,
-html[dir='ltr'] #toolbarViewerRight > * {
+html[dir='ltr'] #toolbarViewerRight > *,
+html[dir='ltr'] .findbar > * {
   float: left;
 }
 html[dir='rtl'] #toolbarViewerLeft > *,
 html[dir='rtl'] #toolbarViewerMiddle > *,
-html[dir='rtl'] #toolbarViewerRight > * {
+html[dir='rtl'] #toolbarViewerRight > *,
+html[dir='rtl'] .findbar > * {
   float: right;
 }
 
 html[dir='ltr'] .splitToolbarButton {
   margin: 3px 2px 4px 0;
   display: inline-block;
 }
 html[dir='rtl'] .splitToolbarButton {
@@ -625,16 +728,36 @@ html[dir='rtl'] .toolbarButton:first-chi
   min-width: 30px;
 }
 
 .toolbarButton#sidebarToggle::before {
   display: inline-block;
   content: url(images/toolbarButton-sidebarToggle.png);
 }
 
+html[dir='ltr'] .toolbarButton.findPrevious::before {
+  display: inline-block;
+  content: url(images/findbarButton-previous.png);
+}
+
+html[dir='rtl'] .toolbarButton.findPrevious::before {
+  display: inline-block;
+  content: url(images/findbarButton-previous-rtl.png);
+}
+
+html[dir='ltr'] .toolbarButton.findNext::before {
+  display: inline-block;
+  content: url(images/findbarButton-next.png);
+}
+
+html[dir='rtl'] .toolbarButton.findNext::before {
+  display: inline-block;
+  content: url(images/findbarButton-next-rtl.png);
+}
+
 html[dir='ltr'] .toolbarButton.pageUp::before {
   display: inline-block;
   content: url(images/toolbarButton-pageUp.png);
 }
 
 html[dir='rtl'] .toolbarButton.pageUp::before {
   display: inline-block;
   content: url(images/toolbarButton-pageUp-rtl.png);
@@ -697,17 +820,17 @@ html[dir='rtl'] .toolbarButton.pageDown:
   content: url(images/toolbarButton-viewThumbnail.png);
 }
 
 #viewOutline.toolbarButton::before {
   display: inline-block;
   content: url(images/toolbarButton-viewOutline.png);
 }
 
-#viewSearch.toolbarButton::before {
+#viewFind.toolbarButton::before {
   display: inline-block;
   content: url(images/toolbarButton-search.png);
 }
 
 
 .toolbarField {
   padding: 3px 6px;
   margin: 4px 0 4px 0;
@@ -724,16 +847,21 @@ html[dir='rtl'] .toolbarButton.pageDown:
   font-size: 12px;
   line-height: 14px;
   outline-style: none;
   -moz-transition-property: background-color, border-color, box-shadow;
   -moz-transition-duration: 150ms;
   -moz-transition-timing-function: ease;
 }
 
+.toolbarField[type=checkbox] {
+  display: inline-block;
+  margin: 8px 0px;
+}
+
 .toolbarField.pageNumber {
   min-width: 16px;
   text-align: right;
   width: 40px;
 }
 
 .toolbarField.pageNumber::-webkit-inner-spin-button,
 .toolbarField.pageNumber::-webkit-outer-spin-button {
@@ -839,35 +967,33 @@ a:focus > .thumbnail > .thumbnailSelecti
   -webkit-user-select:none;
   -moz-user-select:none;
 }
 
 .outlineItem > .outlineItems {
   margin-left: 20px;
 }
 
-.outlineItem > a,
-#searchResults > a {
+.outlineItem > a {
   text-decoration: none;
   display: inline-block;
   min-width: 95%;
   height: 20px;
   padding: 2px 0 0 10px;
   margin-bottom: 1px;
   border-radius: 2px;
   color: hsla(0,0%,100%,.8);
   font-size: 13px;
   line-height: 15px;
   -moz-user-select:none;
   cursor: default;
   white-space: nowrap;
 }
 
-.outlineItem > a:hover,
-#searchResults > a:hover {
+.outlineItem > a:hover {
   background-color: hsla(0,0%,100%,.02);
   background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
   background-clip: padding-box;
   box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset,
               0 0 1px hsla(0,0%,100%,.2) inset,
               0 0 1px hsla(0,0%,0%,.2);
   color: hsla(0,0%,100%,.9);
 }
@@ -884,54 +1010,24 @@ a:focus > .thumbnail > .thumbnailSelecti
 
 .noOutline,
 .noResults {
   font-size: 12px;
   color: hsla(0,0%,100%,.8);
   font-style: italic;
 }
 
-#searchScrollView {
+#findScrollView {
   position: absolute;
   top: 10px;
   bottom: 10px;
   left: 10px;
   width: 280px;
 }
 
-#searchToolbar {
-  padding-left: 0px;
-  right: 0px;
-  padding-top: 0px;
-  padding-bottom: 5px;
-}
-
-#searchToolbar > input {
-  margin-left: 4px;
-  width: 124px;
-}
-
-#searchToolbar button {
-  width: auto;
-  margin: 0;
-  padding: 0 6px;
-  height: 22px;
-}
-
-#searchResults {
-  overflow: auto;
-  position: absolute;
-  top: 30px;
-  bottom: 0px;
-  left: 0px;
-  right: 0;
-  padding: 4px 4px 0;
-  font-size: smaller;
-}
-
 #sidebarControls {
   position:absolute;
   width: 180px;
   height: 32px;
   left: 15px;
   bottom: 35px;
 }
 
@@ -1064,16 +1160,40 @@ canvas {
 
 .textLayer > div {
   color: transparent;
   position: absolute;
   line-height:1.3;
   white-space:pre;
 }
 
+.textLayer .highlight {
+  margin: -1px;
+  padding: 1px;
+
+  background-color: rgba(180, 0, 170, 0.2);
+  border-radius: 4px;
+}
+
+.textLayer .highlight.begin {
+  border-radius: 4px 0px 0px 4px;
+}
+
+.textLayer .highlight.end {
+  border-radius: 0px 4px 4px 0px;
+}
+
+.textLayer .highlight.middle {
+  border-radius: 0px;
+}
+
+.textLayer .highlight.selected {
+  background-color: rgba(0, 100, 0, 0.2);
+}
+
 /* TODO: file FF bug to support ::-moz-selection:window-inactive
    so we can override the opaque grey background when the window is inactive;
    see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */
 ::selection { background:rgba(0,0,255,0.3); }
 ::-moz-selection { background:rgba(0,0,255,0.3); }
 
 .annotComment > div {
   position: absolute;
@@ -1286,17 +1406,17 @@ canvas {
   }
   html[dir='rtl'] .outerCenter {
     float: right;
     right: 180px;
   }
 }
 
 @media all and (max-width: 600px) {
-  #toolbarViewerRight {
+  #toolbarViewerRight, #findbar, #viewFind {
     display: none;
   }
 }
 
 @media all and (max-width: 500px) {
   #scaleSelectContainer, #pageNumberLabel {
     display: none;
   }
--- a/browser/extensions/pdfjs/content/web/viewer.html
+++ b/browser/extensions/pdfjs/content/web/viewer.html
@@ -46,17 +46,17 @@ limitations under the License.
 
 var PDFJS = {};
 
 (function pdfjsWrapper() {
   // Use strict in our context only - users might not want it
   'use strict';
 
   PDFJS.build =
-'e98eba1';
+'c8cf445';
 
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* Copyright 2012 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -352,18 +352,27 @@ var Page = (function PageClosure() {
                   // entry in the dictionary.
                   if (!isValidUrl(url))
                     url = '';
                   item.url = url;
                   break;
                 case 'GoTo':
                   item.dest = a.get('D');
                   break;
+                case 'GoToR':
+                  var url = a.get('F');
+                  // TODO: pdf reference says that GoToR
+                  // can also have 'NewWindow' attribute
+                  if (!isValidUrl(url))
+                    url = '';
+                  item.url = url;
+                  item.dest = a.get('D');
+                  break;
                 default:
-                  TODO('other link types');
+                  TODO('unrecognized link type: ' + a.get('S').name);
               }
             } else if (annotation.has('Dest')) {
               // simple destination link
               var dest = annotation.get('Dest');
               item.dest = isName(dest) ? dest.name : dest;
             }
             break;
           case 'Widget':
@@ -633,18 +642,16 @@ var PDFDocument = (function PDFDocumentC
 
 
 
 // Use only for debugging purposes. This should not be used in any code that is
 // in mozilla master.
 var log = (function() {
   if ('console' in globalScope && 'log' in globalScope['console']) {
     return globalScope['console']['log'].bind(globalScope['console']);
-  } else if ('print' in globalScope) {
-    return globalScope['print'].bind(globalScope);
   } else {
     return function nop() {
     };
   }
 })();
 
 // A notice for devs that will not trigger the fallback UI.  These are good
 // for things that are helpful to devs, such as warning that Workers were
@@ -897,17 +904,17 @@ var Util = PDFJS.Util = (function UtilCl
 
 var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() {
   function PageViewport(viewBox, scale, rotate, offsetX, offsetY) {
     // creating transform to convert pdf coordinate system to the normal
     // canvas like coordinates taking in account scale and rotation
     var centerX = (viewBox[2] + viewBox[0]) / 2;
     var centerY = (viewBox[3] + viewBox[1]) / 2;
     var rotateA, rotateB, rotateC, rotateD;
-    switch (rotate) {
+    switch (rotate % 360) {
       case -180:
       case 180:
         rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1;
         break;
       case -270:
       case 90:
         rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0;
         break;
@@ -1950,17 +1957,18 @@ var WorkerTransport = (function WorkerTr
 var TextRenderingMode = {
   FILL: 0,
   STROKE: 1,
   FILL_STROKE: 2,
   INVISIBLE: 3,
   FILL_ADD_TO_PATH: 4,
   STROKE_ADD_TO_PATH: 5,
   FILL_STROKE_ADD_TO_PATH: 6,
-  ADD_TO_PATH: 7
+  ADD_TO_PATH: 7,
+  ADD_TO_PATH_FLAG: 4
 };
 
 // Minimal font size that would be used during canvas fillText operations.
 var MIN_FONT_SIZE = 1;
 
 function createScratchCanvas(width, height) {
   var canvas = document.createElement('canvas');
   canvas.width = width;
@@ -2336,16 +2344,20 @@ var CanvasGraphics = (function CanvasGra
     },
     save: function CanvasGraphics_save() {
       this.ctx.save();
       var old = this.current;
       this.stateStack.push(old);
       this.current = old.clone();
     },
     restore: function CanvasGraphics_restore() {
+      if ('textClipLayers' in this) {
+        this.completeTextClipping();
+      }
+
       var prev = this.stateStack.pop();
       if (prev) {
         this.current = prev;
         this.ctx.restore();
       }
     },
     transform: function CanvasGraphics_transform(a, b, c, d, e, f) {
       this.ctx.transform(a, b, c, d, e, f);
@@ -2465,16 +2477,74 @@ var CanvasGraphics = (function CanvasGra
 
     // Text
     beginText: function CanvasGraphics_beginText() {
       this.current.textMatrix = IDENTITY_MATRIX;
       this.current.x = this.current.lineX = 0;
       this.current.y = this.current.lineY = 0;
     },
     endText: function CanvasGraphics_endText() {
+      if ('textClipLayers' in this) {
+        this.swapImageForTextClipping();
+      }
+    },
+    getCurrentTextClipping: function CanvasGraphics_getCurrentTextClipping() {
+      var ctx = this.ctx;
+      var transform = ctx.mozCurrentTransform;
+      if ('textClipLayers' in this) {
+        // we need to reset only font and transform
+        var maskCtx = this.textClipLayers.maskCtx;
+        maskCtx.setTransform.apply(maskCtx, transform);
+        maskCtx.font = ctx.font;
+        return maskCtx;
+      }
+
+      var canvasWidth = ctx.canvas.width;
+      var canvasHeight = ctx.canvas.height;
+      // keeping track of the text clipping of the separate canvas
+      var maskCanvas = createScratchCanvas(canvasWidth, canvasHeight);
+      var maskCtx = maskCanvas.getContext('2d');
+      maskCtx.setTransform.apply(maskCtx, transform);
+      maskCtx.font = ctx.font;
+      var textClipLayers = {
+        maskCanvas: maskCanvas,
+        maskCtx: maskCtx
+      };
+      this.textClipLayers = textClipLayers;
+      return maskCtx;
+    },
+    swapImageForTextClipping:
+      function CanvasGraphics_swapImageForTextClipping() {
+      var ctx = this.ctx;
+      var canvasWidth = ctx.canvas.width;
+      var canvasHeight = ctx.canvas.height;
+      // saving current image content and clearing whole canvas
+      ctx.save();
+      ctx.setTransform(1, 0, 0, 1, 0, 0);
+      var data = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
+      this.textClipLayers.imageData = data;
+      ctx.clearRect(0, 0, canvasWidth, canvasHeight);
+      ctx.restore();
+    },
+    completeTextClipping: function CanvasGraphics_completeTextClipping() {
+      var ctx = this.ctx;
+      // applying mask to the image (result is saved in maskCanvas)
+      var maskCtx = this.textClipLayers.maskCtx;
+      maskCtx.setTransform(1, 0, 0, 1, 0, 0);
+      maskCtx.globalCompositeOperation = 'source-in';
+      maskCtx.drawImage(ctx.canvas, 0, 0);
+
+      // restoring image data and applying the result of masked drawing
+      ctx.save();
+      ctx.setTransform(1, 0, 0, 1, 0, 0);
+      ctx.putImageData(this.textClipLayers.imageData, 0, 0);
+      ctx.drawImage(this.textClipLayers.maskCanvas, 0, 0);
+      ctx.restore();
+
+      delete this.textClipLayers;
     },
     setCharSpacing: function CanvasGraphics_setCharSpacing(spacing) {
       this.current.charSpacing = spacing;
     },
     setWordSpacing: function CanvasGraphics_setWordSpacing(spacing) {
       this.current.wordSpacing = spacing;
     },
     setHScale: function CanvasGraphics_setHScale(scale) {
@@ -2532,18 +2602,16 @@ var CanvasGraphics = (function CanvasGra
       var browserFontSize = size >= MIN_FONT_SIZE ? size : MIN_FONT_SIZE;
       this.current.fontSizeScale = browserFontSize != MIN_FONT_SIZE ? 1.0 :
                                    size / MIN_FONT_SIZE;
 
       var rule = italic + ' ' + bold + ' ' + browserFontSize + 'px ' + typeface;
       this.ctx.font = rule;
     },
     setTextRenderingMode: function CanvasGraphics_setTextRenderingMode(mode) {
-      if (mode >= TextRenderingMode.FILL_ADD_TO_PATH)
-        TODO('unsupported text rendering mode: ' + mode);
       this.current.textRenderingMode = mode;
     },
     setTextRise: function CanvasGraphics_setTextRise(rise) {
       this.current.textRise = rise;
     },
     moveText: function CanvasGraphics_moveText(x, y) {
       this.current.x = this.current.lineX += x;
       this.current.y = this.current.lineY += y;
@@ -2568,30 +2636,33 @@ var CanvasGraphics = (function CanvasGra
       var fontMatrix = current.fontMatrix || IDENTITY_MATRIX;
 
       ctx.transform.apply(ctx, current.textMatrix);
       ctx.scale(1, -1);
       ctx.translate(current.x, -current.y - current.textRise);
       ctx.transform.apply(ctx, fontMatrix);
       ctx.scale(textHScale, 1);
     },
-    getTextGeometry: function CanvasGraphics_getTextGeometry() {
+    createTextGeometry: function CanvasGraphics_createTextGeometry() {
       var geometry = {};
       var ctx = this.ctx;
       var font = this.current.font;
       var ctxMatrix = ctx.mozCurrentTransform;
       if (ctxMatrix) {
         var bl = Util.applyTransform([0, 0], ctxMatrix);
         var tr = Util.applyTransform([1, 1], ctxMatrix);
         geometry.x = bl[0];
         geometry.y = bl[1];
         geometry.hScale = tr[0] - bl[0];
         geometry.vScale = tr[1] - bl[1];
       }
       geometry.spaceWidth = font.spaceWidth;
+      geometry.fontName = font.loadedName;
+      geometry.fontFamily = font.fallbackName;
+      geometry.fontSize = this.current.fontSize;
       return geometry;
     },
 
     showText: function CanvasGraphics_showText(str, skipTextSelection) {
       var ctx = this.ctx;
       var current = this.current;
       var font = current.font;
       var glyphs = font.charsToGlyphs(str);
@@ -2615,17 +2686,17 @@ var CanvasGraphics = (function CanvasGra
         ctx.transform.apply(ctx, current.textMatrix);
         ctx.translate(current.x, current.y);
 
         ctx.scale(textHScale, 1);
 
         if (textSelection) {
           this.save();
           ctx.scale(1, -1);
-          geom = this.getTextGeometry();
+          geom = this.createTextGeometry();
           this.restore();
         }
         for (var i = 0; i < glyphsLength; ++i) {
 
           var glyph = glyphs[i];
           if (glyph === null) {
             // word break
             this.ctx.translate(wordSpacing, 0);
@@ -2656,17 +2727,17 @@ var CanvasGraphics = (function CanvasGra
         var lineWidth = current.lineWidth;
         var scale = Math.abs(current.textMatrix[0] * fontMatrix[0]);
         if (scale == 0 || lineWidth == 0)
           lineWidth = this.getSinglePixelWidth();
         else
           lineWidth /= scale;
 
         if (textSelection)
-          geom = this.getTextGeometry();
+          geom = this.createTextGeometry();
 
         if (fontSizeScale != 1.0) {
           ctx.scale(fontSizeScale, fontSizeScale);
           lineWidth /= fontSizeScale;
         }
 
         ctx.lineWidth = lineWidth;
 
@@ -2696,35 +2767,40 @@ var CanvasGraphics = (function CanvasGra
                 ctx.strokeText(character, scaledX, 0);
                 break;
               case TextRenderingMode.FILL_STROKE:
               case TextRenderingMode.FILL_STROKE_ADD_TO_PATH:
                 ctx.fillText(character, scaledX, 0);
                 ctx.strokeText(character, scaledX, 0);
                 break;
               case TextRenderingMode.INVISIBLE:
+              case TextRenderingMode.ADD_TO_PATH:
                 break;
             }
+            if (textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG) {
+              var clipCtx = this.getCurrentTextClipping();
+              clipCtx.fillText(character, scaledX, 0);
+            }
           }
 
           x += charWidth;
 
           var glyphUnicode = glyph.unicode === ' ' ? '\u00A0' : glyph.unicode;
           if (glyphUnicode in NormalizedUnicodes)
             glyphUnicode = NormalizedUnicodes[glyphUnicode];
 
           canvasWidth += charWidth;
         }
         current.x += x * textHScale2;
         ctx.restore();
       }
 
       if (textSelection) {
         geom.canvasWidth = canvasWidth;
-        this.textLayer.appendText(font.fallbackName, fontSize, geom);
+        this.textLayer.appendText(geom);
       }
 
       return canvasWidth;
     },
     showSpacedText: function CanvasGraphics_showSpacedText(arr) {
       var ctx = this.ctx;
       var current = this.current;
       var font = current.font;
@@ -2743,17 +2819,17 @@ var CanvasGraphics = (function CanvasGra
         // Type3 fonts - each glyph is a "mini-PDF" (see also showText)
         if (font.coded) {
           ctx.transform.apply(ctx, current.textMatrix);
           ctx.scale(1, -1);
           ctx.translate(current.x, -1 * current.y);
           ctx.scale(textHScale, 1);
         } else
           this.applyTextTransforms();
-        geom = this.getTextGeometry();
+        geom = this.createTextGeometry();
         ctx.restore();
       }
 
       for (var i = 0; i < arrLength; ++i) {
         var e = arr[i];
         if (isNum(e)) {
           var spacingLength = -e * 0.001 * fontSize * textHScale;
           current.x += spacingLength;
@@ -2767,17 +2843,17 @@ var CanvasGraphics = (function CanvasGra
             canvasWidth += shownCanvasWidth;
         } else {
           error('TJ array element ' + e + ' is not string or num');
         }
       }
 
       if (textSelection) {
         geom.canvasWidth = canvasWidth;
-        this.textLayer.appendText(font.fallbackName, fontSize, geom);
+        this.textLayer.appendText(geom);
       }
     },
     nextLineShowText: function CanvasGraphics_nextLineShowText(text) {
       this.nextLine();
       this.showText(text);
     },
     nextLineSetSpacingShowText:
       function CanvasGraphics_nextLineSetSpacingShowText(wordSpacing,
@@ -3108,18 +3184,32 @@ var CanvasGraphics = (function CanvasGra
       var tmpCanvas = createScratchCanvas(w, h);
       var tmpCtx = tmpCanvas.getContext('2d');
       this.putBinaryImageData(tmpCtx, imgData, w, h);
 
       ctx.drawImage(tmpCanvas, 0, -h);
       this.restore();
     },
 
-    putBinaryImageData: function CanvasGraphics_putBinaryImageData() {
-      //
+    putBinaryImageData: function CanvasGraphics_putBinaryImageData(ctx, imgData,
+                                                                   w, h) {
+      var tmpImgData = 'createImageData' in ctx ? ctx.createImageData(w, h) :
+        ctx.getImageData(0, 0, w, h);
+
+      var tmpImgDataPixels = tmpImgData.data;
+      var data = imgData.data;
+      if ('set' in tmpImgDataPixels)
+        tmpImgDataPixels.set(data);
+      else {
+        // Copy over the imageData pixel by pixel.
+        for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++)
+          tmpImgDataPixels[i] = data[i];
+      }
+
+      ctx.putImageData(tmpImgData, 0, 0);
     },
 
     // Marked content
 
     markPoint: function CanvasGraphics_markPoint(tag) {
       // TODO Marked content.
     },
     markPointProps: function CanvasGraphics_markPointProps(tag, properties) {
@@ -3176,57 +3266,16 @@ var CanvasGraphics = (function CanvasGra
       var inverse = this.ctx.mozCurrentTransformInverse;
       return Math.abs(inverse[0] + inverse[2]);
     }
   };
 
   return CanvasGraphics;
 })();
 
-function checkPutBinaryImageDataCompatibility() {
-  // Feature detection if the browser can use an Uint8Array directly as imgData.
-  var canvas = document.createElement('canvas');
-  canvas.width = 1;
-  canvas.height = 1;
-  var ctx = canvas.getContext('2d');
-
-  try {
-    ctx.putImageData({
-      width: 1,
-      height: 1,
-      data: new Uint8Array(4)
-    }, 0, 0);
-
-    CanvasGraphics.prototype.putBinaryImageData =
-      function CanvasGraphicsPutBinaryImageDataNative(ctx, imgData) {
-        ctx.putImageData(imgData, 0, 0);
-      };
-  } catch (e) {
-    CanvasGraphics.prototype.putBinaryImageData =
-      function CanvasGraphicsPutBinaryImageDataShim(ctx, imgData, w, h) {
-        var tmpImgData = 'createImageData' in ctx ? ctx.createImageData(w, h) :
-          ctx.getImageData(0, 0, w, h);
-
-        var tmpImgDataPixels = tmpImgData.data;
-        var data = imgData.data;
-        if ('set' in tmpImgDataPixels)
-          tmpImgDataPixels.set(data);
-        else {
-          // Copy over the imageData pixel by pixel.
-          for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++)
-            tmpImgDataPixels[i] = data[i];
-        }
-
-        ctx.putImageData(tmpImgData, 0, 0);
-      };
-  }
-}
-if (!isWorker) {
-  checkPutBinaryImageDataCompatibility();
-}
 
 
 var Name = (function NameClosure() {
   function Name(name) {
     this.name = name;
   }
 
   Name.prototype = {};
@@ -13265,33 +13314,39 @@ var PartialEvaluator = (function Partial
 
   PartialEvaluator.prototype = {
     loadFont: function PartialEvaluator_loadFont(fontName, font, xref,
                                                  resources, dependency) {
       var fontRes = resources.get('Font');
 
       assert(fontRes, 'fontRes not available');
 
+      ++this.fontIdCounter;
+
       font = xref.fetchIfRef(font) || fontRes.get(fontName);
-      assertWellFormed(isDict(font));
-
-      ++this.fontIdCounter;
+      if (!isDict(font)) {
+        return {
+          translated: new ErrorFont('Font ' + fontName + ' is not available'),
+          loadedName: 'font_' + this.uniquePrefix + this.fontIdCounter
+        };
+      }
+
       var loadedName = font.loadedName;
       if (!loadedName) {
         // keep track of each font we translated so the caller can
         // load them asynchronously before calling display on a page
         loadedName = 'font_' + this.uniquePrefix + this.fontIdCounter;
         font.loadedName = loadedName;
 
         var translated;
         try {
           translated = this.translateFont(font, xref, resources,
                                           dependency);
         } catch (e) {
-          translated = { error: e };
+          translated = new ErrorFont(e instanceof Error ? e.message : e);
         }
         font.translated = translated;
 
         var data = translated;
         if (data.loadCharProcs) {
           delete data.loadCharProcs;
 
           var charProcs = font.get('CharProcs').getAll();
@@ -13329,20 +13384,17 @@ var PartialEvaluator = (function Partial
         }
       }
 
       function handleSetFont(fontName, font) {
         font = self.loadFont(fontName, font, xref, resources, dependency);
 
         var loadedName = font.loadedName;
         if (!font.sent) {
-          var data = font.translated;
-
-          if (data instanceof Font)
-            data = data.exportData();
+          var data = font.translated.exportData();
 
           handler.send('obj', [
               loadedName,
               'Font',
               data
           ]);
           font.sent = true;
         }
@@ -13630,16 +13682,18 @@ var PartialEvaluator = (function Partial
       }
 
       return queue;
     },
 
     getTextContent: function partialEvaluatorGetIRQueue(
                                                     stream, resources, state) {
       var bidiTexts;
+      var kSpaceFactor = 0.35;
+      var kMultipleSpaceFactor = 1.5;
 
       if (!state) {
         bidiTexts = [];
         state = {
           bidiTexts: bidiTexts
         };
       } else {
         bidiTexts = state.bidiTexts;
@@ -13671,18 +13725,23 @@ var PartialEvaluator = (function Partial
               font = handleSetFont(args[0].name).translated;
               break;
             case 'TJ':
               var items = args[0];
               for (var j = 0, jj = items.length; j < jj; j++) {
                 if (typeof items[j] === 'string') {
                   chunk += fontCharsToUnicode(items[j], font);
                 } else if (items[j] < 0 && font.spaceWidth > 0) {
-                  var numFakeSpaces = Math.round(-items[j] / font.spaceWidth);
-                  if (numFakeSpaces > 0) {
+                  var fakeSpaces = -items[j] / font.spaceWidth;
+                  if (fakeSpaces > kMultipleSpaceFactor) {
+                    fakeSpaces = Math.round(fakeSpaces);
+                    while (fakeSpaces--) {
+                      chunk += ' ';
+                    }
+                  } else if (fakeSpaces > kSpaceFactor) {
                     chunk += ' ';
                   }
                 }
               }
               break;
             case 'Tj':
               chunk += fontCharsToUnicode(args[0], font);
               break;
@@ -16504,65 +16563,68 @@ var Font = (function FontClosure() {
 
     var numTables = 1;
     var cmap = '\x00\x00' + // version
                string16(numTables) +  // numTables
                '\x00\x03' + // platformID
                '\x00\x01' + // encodingID
                string32(4 + numTables * 8); // start of the table record
 
-    var segCount = ranges.length + 1;
+    var trailingRangesCount = ranges[ranges.length - 1][1] < 0xFFFF ? 1 : 0;
+    var segCount = ranges.length + trailingRangesCount;
     var segCount2 = segCount * 2;
     var searchRange = getMaxPower2(segCount) * 2;
     var searchEntry = Math.log(segCount) / Math.log(2);
     var rangeShift = 2 * segCount - searchRange;
 
     // Fill up the 4 parallel arrays describing the segments.
     var startCount = '';
     var endCount = '';
     var idDeltas = '';
     var idRangeOffsets = '';
     var glyphsIds = '';
     var bias = 0;
 
     if (deltas) {
-      for (var i = 0; i < segCount - 1; i++) {
+      for (var i = 0, ii = ranges.length; i < ii; i++) {
         var range = ranges[i];
         var start = range[0];
         var end = range[1];
         var offset = (segCount - i) * 2 + bias * 2;
         bias += (end - start + 1);
 
         startCount += string16(start);
         endCount += string16(end);
         idDeltas += string16(0);
         idRangeOffsets += string16(offset);
 
         var codes = range[2];
         for (var j = 0, jj = codes.length; j < jj; ++j)
           glyphsIds += string16(deltas[codes[j]]);
       }
     } else {
-      for (var i = 0; i < segCount - 1; i++) {
+      for (var i = 0, ii = ranges.length; i < ii; i++) {
         var range = ranges[i];
         var start = range[0];
         var end = range[1];
         var startCode = range[2][0];
 
         startCount += string16(start);
         endCount += string16(end);
         idDeltas += string16((startCode - start + 1) & 0xFFFF);
         idRangeOffsets += string16(0);
       }
     }
 
-    endCount += '\xFF\xFF';
-    startCount += '\xFF\xFF';
-    idDeltas += '\x00\x01';
-    idRangeOffsets += '\x00\x00';
+    if (trailingRangesCount > 0) {
+      endCount += '\xFF\xFF';
+      startCount += '\xFF\xFF';
+      idDeltas += '\x00\x01';
+      idRangeOffsets += '\x00\x00';
+    }
 
     var format314 = '\x00\x00' + // language
                     string16(segCount2) +
                     string16(searchRange) +
                     string16(searchEntry) +
                     string16(rangeShift) +
                     endCount + '\x00\x00' + startCount +
                     idDeltas + idRangeOffsets + glyphsIds;
@@ -17233,16 +17295,19 @@ var Font = (function FontClosure() {
 
         return true;
       }
 
       // Check that required tables are present
       var requiredTables = ['OS/2', 'cmap', 'head', 'hhea',
                              'hmtx', 'maxp', 'name', 'post'];
 
+      var optionalTables = ['cvt ', 'fpgm', 'glyf', 'loca', 'prep',
+                            'CFF ', 'VORG', 'vhea', 'vmtx'];
+
       var header = readOpenTypeHeader(font);
       var numTables = header.numTables;
 
       var cmap, post, maxp, hhea, hmtx, vhea, vmtx, head, loca, glyf, os2;
       var tables = [];
       for (var i = 0; i < numTables; i++) {
         var table = readTableEntry(font);
         var index = requiredTables.indexOf(table.tag);
@@ -17258,16 +17323,19 @@ var Font = (function FontClosure() {
           else if (table.tag == 'hmtx')
             hmtx = table;
           else if (table.tag == 'head')
             head = table;
           else if (table.tag == 'OS/2')
             os2 = table;
 
           requiredTables.splice(index, 1);
+        } else if (optionalTables.indexOf(table.tag) < 0) {
+          // skipping table if it's not a required or optional table
+          continue;
         } else {
           if (table.tag == 'vmtx')
             vmtx = table;
           else if (table.tag == 'vhea')
             vhea = table;
           else if (table.tag == 'loca')
             loca = table;
           else if (table.tag == 'glyf')
@@ -17563,16 +17631,22 @@ var Font = (function FontClosure() {
           }
           this.useToFontChar = true;
         }
 
         createGlyphNameMap(glyphs, ids, properties);
         this.glyphNameMap = properties.glyphNameMap;
       }
 
+      if (glyphs.length === 0) {
+        // defines at least one glyph
+        glyphs.push({ unicode: 0xF000, code: 0xF000, glyph: '.notdef' });
+        ids.push(0);
+      }
+
       // Converting glyphs and ids into font's cmap table
       cmap.data = createCMapTable(glyphs, ids);
       var unicodeIsEnabled = [];
       for (var i = 0, ii = glyphs.length; i < ii; i++) {
         unicodeIsEnabled[glyphs[i].unicode] = true;
       }
       this.unicodeIsEnabled = unicodeIsEnabled;
 
@@ -18075,16 +18149,19 @@ var Font = (function FontClosure() {
 var ErrorFont = (function ErrorFontClosure() {
   function ErrorFont(error) {
     this.error = error;
   }
 
   ErrorFont.prototype = {
     charsToGlyphs: function ErrorFont_charsToGlyphs() {
       return [];
+    },
+    exportData: function ErrorFont_exportData() {
+      return {error: this.error};
     }
   };
 
   return ErrorFont;
 })();
 
 var CallothersubrCmd = (function CallothersubrCmdClosure() {
   function CallothersubrCmd(index) {
@@ -18298,16 +18375,31 @@ var Type1Parser = function type1Parser()
               charstring.push(3);
               i++;
               continue;
             }
 
             assert(argc == 0, 'callothersubr with arguments is not supported');
             charstring.push(new CallothersubrCmd(index));
             continue;
+          } else if (escape == 7) { // sbw
+            var args = breakUpArgs(charstring, 4);
+            var arg0 = args[0];
+            var arg1 = args[1];
+            var arg2 = args[2];
+            lsb = arg0.value;
+            width = arg2.value;
+            // To convert to type2 we have to move the width value to the first
+            // part of the charstring and then use rmoveto with (dx, dy).
+            // The height argument will not be used for vmtx and vhea tables
+            // reconstruction -- ignoring it.
+            charstring = arg2.arg;
+            charstring = charstring.concat(arg0.arg, arg1.arg);
+            charstring.push('rmoveto');
+            continue;
           } else if (escape == 17 || escape == 33) {
             // pop or setcurrentpoint commands can be ignored
             // since we are not doing callothersubr
             continue;
           } else if (escape == 6) {
             // seac is like type 2's special endchar but it doesn't use the
             // first argument asb, so remove it.
             var args = breakUpArgs(charstring, 5);
@@ -19105,20 +19197,19 @@ var CFFFont = (function CFFFontClosure()
       var unassignedUnicodeItems = [];
       var inverseEncoding = [];
       // CID fonts don't have an encoding.
       if (encoding !== null)
         for (var charcode in encoding)
           inverseEncoding[encoding[charcode]] = charcode | 0;
       else
         inverseEncoding = charsets;
-      for (var i = 0, ii = charsets.length; i < ii; i++) {
+      var i = charsets[0] == '.notdef' ? 1 : 0;
+      for (var ii = charsets.length; i < ii; i++) {
         var glyph = charsets[i];
-        if (glyph == '.notdef')
-          continue;
 
         var code = inverseEncoding[i];
         if (!code || isSpecialUnicode(code)) {
           unassignedUnicodeItems.push(i);
           continue;
         }
         charstrings.push({
           unicode: code,
@@ -31240,16 +31331,17 @@ function MessageHandler(name, comObj) {
   // If there's no console available, console_error in the
   // action handler will do nothing.
   if ('console' in globalScope) {
     ah['console_error'] = [function ahConsoleError(data) {
       globalScope['console'].error.apply(null, data);
     }];
   } else {
     ah['console_error'] = [function ahConsoleError(data) {
+      log.apply(null, data);
     }];
   }
   ah['_warn'] = [function ah_Warn(data) {
     warn(data);
   }];
 
   comObj.onmessage = function messageHandlerComObjOnMessage(event) {
     var data = event.data;
@@ -35955,45 +36047,55 @@ var JpegImage = (function jpegImage() {
       <div id="sidebarContainer">
         <div id="toolbarSidebar" class="splitToolbarButton toggled">
           <button id="viewThumbnail" class="toolbarButton group toggled" title="Show Thumbnails" tabindex="1" data-l10n-id="thumbs">
              <span data-l10n-id="thumbs_label">Thumbnails</span>
           </button>
           <button id="viewOutline" class="toolbarButton group" title="Show Document Outline" tabindex="2" data-l10n-id="outline">
              <span data-l10n-id="outline_label">Document Outline</span>
           </button>
-          <button id="viewSearch" class="toolbarButton group hidden" title="Search Document" tabindex="3" data-l10n-id="search_panel">
-             <span data-l10n-id="search_panel_label">Search Document</span>
-          </button>
         </div>
         <div id="sidebarContent">
           <div id="thumbnailView">
           </div>
           <div id="outlineView" class="hidden">
           </div>
-          <div id="searchView" class="hidden">
-            <div id="searchToolbar">
-              <input id="searchTermsInput" class="toolbarField">
-              <button id="searchButton" class="textButton toolbarButton" data-l10n-id="search">Find</button>
-            </div>
-            <div id="searchResults"></div>
-          </div>
         </div>
       </div>  <!-- sidebarContainer -->
 
       <div id="mainContainer">
+        <div class="findbar hidden doorHanger" id="findbar">
+          <label for="findInput" class="toolbarLabel" data-l10n-id="find_label">Find:</label>
+          <input id="findInput" class="toolbarField" tabindex="20">
+          <div class="splitToolbarButton">
+            <button class="toolbarButton findPrevious" title="" id="findPrevious" tabindex="21" data-l10n-id="find_previous">
+              <span data-l10n-id="find_previous_label">Previous</span>
+            </button>
+            <div class="splitToolbarButtonSeparator"></div>
+            <button class="toolbarButton findNext" title="" id="findNext" tabindex="22" data-l10n-id="find_next">
+              <span data-l10n-id="find_next_label">Next</span>
+            </button>
+          </div>
+          <input type="checkbox" id="findHighlightAll" class="toolbarField">
+          <label for="findHighlightAll" class="toolbarLabel" tabindex="23" data-l10n-id="find_highlight">Highlight all</label>
+          <input type="checkbox" id="findMatchCase" class="toolbarField">
+          <label for="findMatchCase" class="toolbarLabel" tabindex="24" data-l10n-id="find_match_case_label">Match case</label>
+          <span id="findMsg" class="toolbarLabel"></span>
+        </div>
         <div class="toolbar">
           <div id="toolbarContainer">
-
             <div id="toolbarViewer">
               <div id="toolbarViewerLeft">
-                <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="4" data-l10n-id="toggle_slider">
+                <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="3" data-l10n-id="toggle_slider">
                   <span data-l10n-id="toggle_slider_label">Toggle Sidebar</span>
                 </button>
                 <div class="toolbarButtonSpacer"></div>
+                <button id="viewFind" class="toolbarButton group" title="Find in Document" tabindex="4" data-l10n-id="findbar">
+                   <span data-l10n-id="findbar_label">Find</span>
+                </button>
                 <div class="splitToolbarButton">
                   <button class="toolbarButton pageUp" title="Previous Page" id="previous" tabindex="5" data-l10n-id="previous">
                     <span data-l10n-id="previous_label">Previous</span>
                   </button>
                   <div class="splitToolbarButtonSeparator"></div>
                   <button class="toolbarButton pageDown" title="Next Page" id="next" tabindex="6" data-l10n-id="next">
                     <span data-l10n-id="next_label">Next</span>
                   </button>
@@ -36053,16 +36155,20 @@ var JpegImage = (function jpegImage() {
                   </span>
                 </div>
               </div>
             </div>
           </div>
         </div>
 
         <menu type="context" id="viewerContextMenu">
+          <menuitem label="First Page" id="first_page"
+                    data-l10n-id="first_page" ></menuitem>
+          <menuitem label="Last Page" id="last_page"
+                    data-l10n-id="last_page" ></menuitem>
           <menuitem label="Rotate Counter-Clockwise" id="page_rotate_ccw"
                     data-l10n-id="page_rotate_ccw" ></menuitem>
           <menuitem label="Rotate Clockwise" id="page_rotate_cw"
                     data-l10n-id="page_rotate_cw" ></menuitem>
         </menu>
 
         <div id="viewerContainer">
           <div id="viewer" contextmenu="viewerContextMenu"></div>
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -19,26 +19,33 @@
 
 var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf';
 var kDefaultScale = 'auto';
 var kDefaultScaleDelta = 1.1;
 var kUnknownScale = 0;
 var kCacheSize = 20;
 var kCssUnits = 96.0 / 72.0;
 var kScrollbarPadding = 40;
+var kVerticalPadding = 5;
 var kMinScale = 0.25;
 var kMaxScale = 4.0;
 var kImageDirectory = './images/';
 var kSettingsMemory = 20;
 var RenderingStates = {
   INITIAL: 0,
   RUNNING: 1,
   PAUSED: 2,
   FINISHED: 3
 };
+var FindStates = {
+  FIND_FOUND: 0,
+  FIND_NOTFOUND: 1,
+  FIND_WRAPPED: 2,
+  FIND_PENDING: 3
+};
 
 
 var mozL10n = document.mozL10n || document.webL10n;
 
 function getFileName(url) {
   var anchor = url.indexOf('#');
   var query = url.indexOf('?');
   var end = Math.min(
@@ -200,77 +207,479 @@ var FirefoxCom = (function FirefoxComClo
     }
   };
 })();
 
 
 // Settings Manager - This is a utility for saving settings
 // First we see if localStorage is available
 // If not, we use FUEL in FF
+// Use asyncStorage for B2G
 var Settings = (function SettingsClosure() {
   var isLocalStorageEnabled = (function localStorageEnabledTest() {
     // Feature test as per http://diveintohtml5.info/storage.html
     // The additional localStorage call is to get around a FF quirk, see
     // bug #495747 in bugzilla
     try {
       return 'localStorage' in window && window['localStorage'] !== null &&
           localStorage;
     } catch (e) {
       return false;
     }
   })();
 
   function Settings(fingerprint) {
-    var database = null;
-    var index;
-    database = FirefoxCom.requestSync('getDatabase', null) || '{}';
-
-    database = JSON.parse(database);
-    if (!('files' in database))
-      database.files = [];
-    if (database.files.length >= kSettingsMemory)
-      database.files.shift();
-    for (var i = 0, length = database.files.length; i < length; i++) {
-      var branch = database.files[i];
-      if (branch.fingerprint == fingerprint) {
-        index = i;
-        break;
-      }
-    }
-    if (typeof index != 'number')
-      index = database.files.push({fingerprint: fingerprint}) - 1;
-    this.file = database.files[index];
-    this.database = database;
+    this.fingerprint = fingerprint;
+    this.initializedPromise = new PDFJS.Promise();
+
+    var resolvePromise = (function settingsResolvePromise(db) {
+      this.initialize(db || '{}');
+      this.initializedPromise.resolve();
+    }).bind(this);
+
+
+    resolvePromise(FirefoxCom.requestSync('getDatabase', null));
+
   }
 
   Settings.prototype = {
+    initialize: function settingsInitialize(database) {
+      database = JSON.parse(database);
+      if (!('files' in database))
+        database.files = [];
+      if (database.files.length >= kSettingsMemory)
+        database.files.shift();
+      var index;
+      for (var i = 0, length = database.files.length; i < length; i++) {
+        var branch = database.files[i];
+        if (branch.fingerprint == this.fingerprint) {
+          index = i;
+          break;
+        }
+      }
+      if (typeof index != 'number')
+        index = database.files.push({fingerprint: this.fingerprint}) - 1;
+      this.file = database.files[index];
+      this.database = database;
+    },
+
     set: function settingsSet(name, val) {
-      if (!('file' in this))
-        return false;
+      if (!this.initializedPromise.isResolved)
+        return;
 
       var file = this.file;
       file[name] = val;
       var database = JSON.stringify(this.database);
       FirefoxCom.requestSync('setDatabase', database);
     },
 
     get: function settingsGet(name, defaultValue) {
-      if (!('file' in this))
+      if (!this.initializedPromise.isResolved)
         return defaultValue;
 
       return this.file[name] || defaultValue;
     }
   };
 
   return Settings;
 })();
 
 var cache = new Cache(kCacheSize);
 var currentPageNumber = 1;
 
+var PDFFindController = {
+  extractTextPromise: null,
+
+  // If active, find results will be highlighted.
+  active: false,
+
+  // Stores the text for each page.
+  pageContents: [],
+
+  pageMatches: [],
+
+  selected: {
+    pageIdx: 0,
+    matchIdx: 0
+  },
+
+  state: null,
+
+  dirtyMatch: false,
+
+  findTimeout: null,
+
+  initialize: function() {
+    var events = [
+      'find',
+      'findagain',
+      'findhighlightallchange',
+      'findcasesensitivitychange'
+    ];
+
+    this.handleEvent = this.handleEvent.bind(this);
+
+    for (var i = 0; i < events.length; i++) {
+      window.addEventListener(events[i], this.handleEvent);
+    }
+  },
+
+  calcFindMatch: function(pageContent) {
+    var query = this.state.query;
+    var caseSensitive = this.state.caseSensitive;
+    var queryLen = query.length;
+
+    if (queryLen === 0)
+      return [];
+
+    if (!caseSensitive) {
+      pageContent = pageContent.toLowerCase();
+      query = query.toLowerCase();
+    }
+
+    var matches = [];
+
+    var matchIdx = -queryLen;
+    while (true) {
+      matchIdx = pageContent.indexOf(query, matchIdx + queryLen);
+      if (matchIdx === -1) {
+        break;
+      }
+
+      matches.push(matchIdx);
+    }
+    return matches;
+  },
+
+  extractText: function() {
+    if (this.extractTextPromise) {
+      return this.extractTextPromise;
+    }
+    this.extractTextPromise = new PDFJS.Promise();
+
+    var self = this;
+    function extractPageText(pageIndex) {
+      PDFView.pages[pageIndex].getTextContent().then(
+        function textContentResolved(data) {
+          // Build the find string.
+          var bidiTexts = data.bidiTexts;
+          var str = '';
+
+          for (var i = 0; i < bidiTexts.length; i++) {
+            str += bidiTexts[i].str;
+          }
+
+          // Store the pageContent as a string.
+          self.pageContents.push(str);
+          // Ensure there is a empty array of matches.
+          self.pageMatches.push([]);
+
+          if ((pageIndex + 1) < PDFView.pages.length)
+            extractPageText(pageIndex + 1);
+          else
+            self.extractTextPromise.resolve();
+        }
+      );
+    }
+    extractPageText(0);
+    return this.extractTextPromise;
+  },
+
+  handleEvent: function(e) {
+    if (this.state === null || e.type !== 'findagain') {
+      this.dirtyMatch = true;
+    }
+    this.state = e.detail;
+    this.updateUIState(FindStates.FIND_PENDING);
+
+    var promise = this.extractText();
+
+    clearTimeout(this.findTimeout);
+    if (e.type === 'find') {
+      // Only trigger the find action after 250ms of silence.
+      this.findTimeout = setTimeout(function() {
+        promise.then(this.performFind.bind(this));
+      }.bind(this), 250);
+    } else {
+      promise.then(this.performFind.bind(this));
+    }
+  },
+
+  updatePage: function(idx) {
+    var page = PDFView.pages[idx];
+
+    if (this.selected.pageIdx === idx) {
+      // If the page is selected, scroll the page into view, which triggers
+      // rendering the page, which adds the textLayer. Once the textLayer is
+      // build, it will scroll onto the selected match.
+      page.scrollIntoView();
+    }
+
+    if (page.textLayer) {
+      page.textLayer.updateMatches();
+    }
+  },
+
+  performFind: function() {
+    // Recalculate all the matches.
+    // TODO: Make one match show up as the current match
+
+    var pages = PDFView.pages;
+    var pageContents = this.pageContents;
+    var pageMatches = this.pageMatches;
+
+    this.active = true;
+
+    if (this.dirtyMatch) {
+      // Need to recalculate the matches.
+      this.dirtyMatch = false;
+
+      this.selected = {
+        pageIdx: -1,
+        matchIdx: -1
+      };
+
+      // TODO: Make this way more lasily (aka. efficient) - e.g. calculate only
+      // the matches for the current visible pages.
+      var firstMatch = true;
+      for (var i = 0; i < pageContents.length; i++) {
+        var matches = pageMatches[i] = this.calcFindMatch(pageContents[i]);
+        if (firstMatch && matches.length !== 0) {
+          firstMatch = false;
+          this.selected = {
+            pageIdx: i,
+            matchIdx: 0
+          };
+        }
+        this.updatePage(i, true);
+      }
+      if (!firstMatch || !this.state.query) {
+        this.updateUIState(FindStates.FIND_FOUND);
+      } else {
+        this.updateUIState(FindStates.FIND_NOTFOUND);
+      }
+    } else {
+      // If there is NO selection, then there is no match at all -> no sense to
+      // handle previous/next action.
+      if (this.selected.pageIdx === -1) {
+        this.updateUIState(FindStates.FIND_NOTFOUND);
+        return;
+      }
+
+      // Handle findAgain case.
+      var previous = this.state.findPrevious;
+      var sPageIdx = this.selected.pageIdx;
+      var sMatchIdx = this.selected.matchIdx;
+      var findState = FindStates.FIND_FOUND;
+
+      if (previous) {
+        // Select previous match.
+
+        if (sMatchIdx !== 0) {
+          this.selected.matchIdx -= 1;
+        } else {
+          var len = pageMatches.length;
+          for (var i = sPageIdx - 1; i != sPageIdx; i--) {
+            if (i < 0)
+              i += len;
+
+            if (pageMatches[i].length !== 0) {
+              this.selected = {
+                pageIdx: i,
+                matchIdx: pageMatches[i].length - 1
+              };
+              break;
+            }
+          }
+          // If pageIdx stayed the same, select last match on the page.
+          if (this.selected.pageIdx === sPageIdx) {
+            this.selected.matchIdx = pageMatches[sPageIdx].length - 1;
+            findState = FindStates.FIND_WRAPPED;
+          } else if (this.selected.pageIdx > sPageIdx) {
+            findState = FindStates.FIND_WRAPPED;
+          }
+        }
+      } else {
+        // Select next match.
+
+        if (pageMatches[sPageIdx].length !== sMatchIdx + 1) {
+          this.selected.matchIdx += 1;
+        } else {
+          var len = pageMatches.length;
+          for (var i = sPageIdx + 1; i < len + sPageIdx; i++) {
+            if (pageMatches[i % len].length !== 0) {
+              this.selected = {
+                pageIdx: i % len,
+                matchIdx: 0
+              };
+              break;
+            }
+          }
+
+          // If pageIdx stayed the same, select first match on the page.
+          if (this.selected.pageIdx === sPageIdx) {
+            this.selected.matchIdx = 0;
+            findState = FindStates.FIND_WRAPPED;
+          } else if (this.selected.pageIdx < sPageIdx) {
+            findState = FindStates.FIND_WRAPPED;
+          }
+        }
+      }
+
+      this.updateUIState(findState, previous);
+      this.updatePage(sPageIdx, sPageIdx === this.selected.pageIdx);
+      if (sPageIdx !== this.selected.pageIdx) {
+        this.updatePage(this.selected.pageIdx, true);
+      }
+    }
+  },
+
+  updateUIState: function(state, previous) {
+    if (PDFView.supportsIntegratedFind) {
+      FirefoxCom.request('updateFindControlState',
+                         {result: state, findPrevious: previous});
+      return;
+    }
+    PDFFindBar.updateUIState(state, previous);
+  }
+};
+
+var PDFFindBar = {
+  // TODO: Enable the FindBar *AFTER* the pagesPromise in the load function
+  // got resolved
+
+  opened: false,
+
+  initialize: function() {
+    this.bar = document.getElementById('findbar');
+    this.toggleButton = document.getElementById('viewFind');
+    this.findField = document.getElementById('findInput');
+    this.highlightAll = document.getElementById('findHighlightAll');
+    this.caseSensitive = document.getElementById('findMatchCase');
+    this.findMsg = document.getElementById('findMsg');
+    this.findStatusIcon = document.getElementById('findStatusIcon');
+
+    var self = this;
+    this.toggleButton.addEventListener('click', function() {
+      self.toggle();
+    });
+
+    this.findField.addEventListener('input', function() {
+      self.dispatchEvent('');
+    });
+
+    this.bar.addEventListener('keydown', function(evt) {
+      switch (evt.keyCode) {
+        case 13: // Enter
+          if (evt.target === self.findField) {
+            self.dispatchEvent('again', evt.shiftKey);
+          }
+          break;
+        case 27: // Escape
+          self.close();
+          break;
+      }
+    });
+
+    document.getElementById('findPrevious').addEventListener('click',
+      function() { self.dispatchEvent('again', true); }
+    );
+
+    document.getElementById('findNext').addEventListener('click', function() {
+      self.dispatchEvent('again', false);
+    });
+
+    this.highlightAll.addEventListener('click', function() {
+      self.dispatchEvent('highlightallchange');
+    });
+
+    this.caseSensitive.addEventListener('click', function() {
+      self.dispatchEvent('casesensitivitychange');
+    });
+  },
+
+  dispatchEvent: function(aType, aFindPrevious) {
+    var event = document.createEvent('CustomEvent');
+    event.initCustomEvent('find' + aType, true, true, {
+      query: this.findField.value,
+      caseSensitive: this.caseSensitive.checked,
+      highlightAll: this.highlightAll.checked,
+      findPrevious: aFindPrevious
+    });
+    return window.dispatchEvent(event);
+  },
+
+  updateUIState: function(state, previous) {
+    var notFound = false;
+    var findMsg = '';
+    var status = '';
+
+    switch (state) {
+      case FindStates.FIND_FOUND:
+        break;
+
+      case FindStates.FIND_PENDING:
+        status = 'pending';
+        break;
+
+      case FindStates.FIND_NOTFOUND:
+        findMsg = mozL10n.get('find_not_found', null, 'Phrase not found');
+        notFound = true;
+        break;
+
+      case FindStates.FIND_WRAPPED:
+        if (previous) {
+          findMsg = mozL10n.get('find_wrapped_to_bottom', null,
+                                'Reached end of page, continued from bottom');
+        } else {
+          findMsg = mozL10n.get('find_wrapped_to_top', null,
+                                'Reached end of page, continued from top');
+        }
+        break;
+    }
+
+    if (notFound) {
+      this.findField.classList.add('notFound');
+    } else {
+      this.findField.classList.remove('notFound');
+    }
+
+    this.findField.setAttribute('data-status', status);
+    this.findMsg.textContent = findMsg;
+  },
+
+  open: function() {
+    if (this.opened) return;
+
+    this.opened = true;
+    this.toggleButton.classList.add('toggled');
+    this.bar.classList.remove('hidden');
+    this.findField.select();
+    this.findField.focus();
+  },
+
+  close: function() {
+    if (!this.opened) return;
+
+    this.opened = false;
+    this.toggleButton.classList.remove('toggled');
+    this.bar.classList.add('hidden');
+
+    PDFFindController.active = false;
+  },
+
+  toggle: function() {
+    if (this.opened) {
+      this.close();
+    } else {
+      this.open();
+    }
+  }
+};
+
 var PDFView = {
   pages: [],
   thumbnails: [],
   currentScale: kUnknownScale,
   currentScaleValue: null,
   initialBookmark: document.location.hash.substring(1),
   startedTextExtraction: false,
   pageText: [],
@@ -280,31 +689,36 @@ var PDFView = {
   fellback: false,
   pdfDocument: null,
   sidebarOpen: false,
   pageViewScroll: null,
   thumbnailViewScroll: null,
   isFullscreen: false,
   previousScale: null,
   pageRotation: 0,
+  mouseScrollTimeStamp: 0,
+  mouseScrollDelta: 0,
   lastScroll: 0,
 
   // called once when the document is loaded
   initialize: function pdfViewInitialize() {
     var self = this;
     var container = this.container = document.getElementById('viewerContainer');
     this.pageViewScroll = {};
     this.watchScroll(container, this.pageViewScroll, updateViewarea);
 
     var thumbnailContainer = this.thumbnailContainer =
                              document.getElementById('thumbnailView');
     this.thumbnailViewScroll = {};
     this.watchScroll(thumbnailContainer, this.thumbnailViewScroll,
                      this.renderHighestPriority.bind(this));
 
+    PDFFindBar.initialize();
+    PDFFindController.initialize();
+
     this.initialized = true;
     container.addEventListener('scroll', function() {
       self.lastScroll = Date.now();
     }, false);
   },
 
   // Helper function to keep track whether a div was scrolled up or down and
   // then call a callback.
@@ -351,20 +765,23 @@ var PDFView = {
     this.currentScaleValue = value;
     if (scale) {
       this.setScale(scale, true, noScroll);
       return;
     }
 
     var container = this.container;
     var currentPage = this.pages[this.page - 1];
+    if (!currentPage) {
+      return;
+    }
 
     var pageWidthScale = (container.clientWidth - kScrollbarPadding) /
                           currentPage.width * currentPage.scale / kCssUnits;
-    var pageHeightScale = (container.clientHeight - kScrollbarPadding) /
+    var pageHeightScale = (container.clientHeight - kVerticalPadding) /
                            currentPage.height * currentPage.scale / kCssUnits;
     switch (value) {
       case 'page-actual':
         scale = 1;
         break;
       case 'page-width':
         scale = pageWidthScale;
         break;
@@ -445,16 +862,26 @@ var PDFView = {
                   doc.webkitRequestFullScreen;
     Object.defineProperty(this, 'supportsFullScreen', { value: support,
                                                         enumerable: true,
                                                         configurable: true,
                                                         writable: false });
     return support;
   },
 
+  get supportsIntegratedFind() {
+    var support = false;
+    support = FirefoxCom.requestSync('supportsIntegratedFind');
+    Object.defineProperty(this, 'supportsIntegratedFind', { value: support,
+                                                            enumerable: true,
+                                                            configurable: true,
+                                                            writable: false });
+    return support;
+  },
+
   initPassiveLoading: function pdfViewInitPassiveLoading() {
     if (!PDFView.loadingBar) {
       PDFView.loadingBar = new ProgressBar('#loadingBar', {});
     }
 
     window.addEventListener('message', function window_message(e) {
       var args = e.data;
 
@@ -705,30 +1132,23 @@ var PDFView = {
       clearInterval(thumbsView._loadingInterval);
 
     var container = document.getElementById('viewer');
     while (container.hasChildNodes())
       container.removeChild(container.lastChild);
 
     var pagesCount = pdfDocument.numPages;
     var id = pdfDocument.fingerprint;
-    var storedHash = null;
     document.getElementById('numPages').textContent =
       mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}');
     document.getElementById('pageNumber').max = pagesCount;
+
     PDFView.documentFingerprint = id;
     var store = PDFView.store = new Settings(id);
-    if (store.get('exists', false)) {
-      var page = store.get('page', '1');
-      var zoom = store.get('zoom', PDFView.currentScale);
-      var left = store.get('scrollLeft', '0');
-      var top = store.get('scrollTop', '0');
-
-      storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top;
-    }
+    var storePromise = store.initializedPromise;
 
     this.pageRotation = 0;
 
     var pages = this.pages = [];
     this.pageText = [];
     this.startedTextExtraction = false;
     var pagesRefMap = {};
     var thumbnails = this.thumbnails = [];
@@ -755,21 +1175,32 @@ var PDFView = {
     });
 
     var destinationsPromise = pdfDocument.getDestinations();
     destinationsPromise.then(function(destinations) {
       self.destinations = destinations;
     });
 
     // outline and initial view depends on destinations and pagesRefMap
-    PDFJS.Promise.all([pagesPromise, destinationsPromise]).then(function() {
+    var promises = [pagesPromise, destinationsPromise, storePromise];
+    PDFJS.Promise.all(promises).then(function() {
       pdfDocument.getOutline().then(function(outline) {
         self.outline = new DocumentOutlineView(outline);
       });
 
+      var storedHash = null;
+      if (store.get('exists', false)) {
+        var page = store.get('page', '1');
+        var zoom = store.get('zoom', PDFView.currentScale);
+        var left = store.get('scrollLeft', '0');
+        var top = store.get('scrollTop', '0');
+
+        storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top;
+      }
+
       self.setInitialView(storedHash, scale);
     });
 
     pdfDocument.getMetadata().then(function(data) {
       var info = data.info, metadata = data.metadata;
       self.documentInfo = info;
       self.metadata = metadata;
 
@@ -887,82 +1318,16 @@ var PDFView = {
       case RenderingStates.INITIAL:
         PDFView.highestPriorityPage = type + view.id;
         view.draw(this.renderHighestPriority.bind(this));
         break;
     }
     return true;
   },
 
-  search: function pdfViewStartSearch() {
-    // Limit this function to run every <SEARCH_TIMEOUT>ms.
-    var SEARCH_TIMEOUT = 250;
-    var lastSearch = this.lastSearch;
-    var now = Date.now();
-    if (lastSearch && (now - lastSearch) < SEARCH_TIMEOUT) {
-      if (!this.searchTimer) {
-        this.searchTimer = setTimeout(function resumeSearch() {
-            PDFView.search();
-          },
-          SEARCH_TIMEOUT - (now - lastSearch)
-        );
-      }
-      return;
-    }
-    this.searchTimer = null;
-    this.lastSearch = now;
-
-    function bindLink(link, pageNumber) {
-      link.href = '#' + pageNumber;
-      link.onclick = function searchBindLink() {
-        PDFView.page = pageNumber;
-        return false;
-      };
-    }
-
-    var searchResults = document.getElementById('searchResults');
-
-    var searchTermsInput = document.getElementById('searchTermsInput');
-    searchResults.removeAttribute('hidden');
-    searchResults.textContent = '';
-
-    var terms = searchTermsInput.value;
-
-    if (!terms)
-      return;
-
-    // simple search: removing spaces and hyphens, then scanning every
-    terms = terms.replace(/\s-/g, '').toLowerCase();
-    var index = PDFView.pageText;
-    var pageFound = false;
-    for (var i = 0, ii = index.length; i < ii; i++) {
-      var pageText = index[i].replace(/\s-/g, '').toLowerCase();
-      var j = pageText.indexOf(terms);
-      if (j < 0)
-        continue;
-
-      var pageNumber = i + 1;
-      var textSample = index[i].substr(j, 50);
-      var link = document.createElement('a');
-      bindLink(link, pageNumber);
-      link.textContent = 'Page ' + pageNumber + ': ' + textSample;
-      searchResults.appendChild(link);
-
-      pageFound = true;
-    }
-    if (!pageFound) {
-      searchResults.textContent = '';
-      var noResults = document.createElement('div');
-      noResults.classList.add('noResults');
-      noResults.textContent = mozL10n.get('search_terms_not_found', null,
-                                              '(Not found)');
-      searchResults.appendChild(noResults);
-    }
-  },
-
   setHash: function pdfViewSetHash(hash) {
     if (!hash)
       return;
 
     if (hash.indexOf('=') >= 0) {
       var params = PDFView.parseQueryString(hash);
       // borrowing syntax from "Parameters for Opening PDF Files"
       if ('nameddest' in params) {
@@ -994,80 +1359,42 @@ var PDFView = {
       this.page = hash;
     else // named destination
       PDFView.navigateTo(unescape(hash));
   },
 
   switchSidebarView: function pdfViewSwitchSidebarView(view) {
     var thumbsView = document.getElementById('thumbnailView');
     var outlineView = document.getElementById('outlineView');
-    var searchView = document.getElementById('searchView');
 
     var thumbsButton = document.getElementById('viewThumbnail');
     var outlineButton = document.getElementById('viewOutline');
-    var searchButton = document.getElementById('viewSearch');
 
     switch (view) {
       case 'thumbs':
         thumbsButton.classList.add('toggled');
         outlineButton.classList.remove('toggled');
-        searchButton.classList.remove('toggled');
         thumbsView.classList.remove('hidden');
         outlineView.classList.add('hidden');
-        searchView.classList.add('hidden');
 
         PDFView.renderHighestPriority();
         break;
 
       case 'outline':
         thumbsButton.classList.remove('toggled');
         outlineButton.classList.add('toggled');
-        searchButton.classList.remove('toggled');
         thumbsView.classList.add('hidden');
         outlineView.classList.remove('hidden');
-        searchView.classList.add('hidden');
 
         if (outlineButton.getAttribute('disabled'))
           return;
         break;
-
-      case 'search':
-        thumbsButton.classList.remove('toggled');
-        outlineButton.classList.remove('toggled');
-        searchButton.classList.add('toggled');
-        thumbsView.classList.add('hidden');
-        outlineView.classList.add('hidden');
-        searchView.classList.remove('hidden');
-
-        var searchTermsInput = document.getElementById('searchTermsInput');
-        searchTermsInput.focus();
-        // Start text extraction as soon as the search gets displayed.
-        this.extractText();
-        break;
     }
   },
 
-  extractText: function() {
-    if (this.startedTextExtraction)
-      return;
-    this.startedTextExtraction = true;
-    var self = this;
-    function extractPageText(pageIndex) {
-      self.pages[pageIndex].pdfPage.getTextContent().then(
-        function textContentResolved(textContent) {
-          self.pageText[pageIndex] = textContent.join('');
-          self.search();
-          if ((pageIndex + 1) < self.pages.length)
-            extractPageText(pageIndex + 1);
-        }
-      );
-    }
-    extractPageText(0);
-  },
-
   getVisiblePages: function pdfViewGetVisiblePages() {
     return this.getVisibleElements(this.container,
                                    this.pages, true);
   },
 
   getVisibleThumbs: function pdfViewGetVisibleThumbs() {
     return this.getVisibleElements(this.thumbnailContainer,
                                    this.thumbnails);
@@ -1188,23 +1515,51 @@ var PDFView = {
     this.previousScale = this.currentScaleValue;
     this.parseScale('page-fit', true);
 
     // Wait for fullscreen to take effect
     setTimeout(function() {
       currentPage.scrollIntoView();
     }, 0);
 
+    this.showPresentationControls();
     return true;
   },
 
   exitFullscreen: function pdfViewExitFullscreen() {
     this.isFullscreen = false;
     this.parseScale(this.previousScale);
     this.page = this.page;
+    this.clearMouseScrollState();
+    this.hidePresentationControls();
+  },
+
+  showPresentationControls: function pdfViewShowPresentationControls() {
+    var DELAY_BEFORE_HIDING_CONTROLS = 3000;
+    var wrapper = document.getElementById('viewerContainer');
+    if (this.presentationControlsTimeout) {
+      clearTimeout(this.presentationControlsTimeout);
+    } else {
+      wrapper.classList.add('presentationControls');
+    }
+    this.presentationControlsTimeout = setTimeout(function hideControls() {
+      wrapper.classList.remove('presentationControls');
+      delete PDFView.presentationControlsTimeout;
+    }, DELAY_BEFORE_HIDING_CONTROLS);
+  },
+
+  hidePresentationControls: function pdfViewShowPresentationControls() {
+    if (!this.presentationControlsTimeout) {
+      return;
+    }
+    clearTimeout(this.presentationControlsTimeout);
+    delete this.presentationControlsTimeout;
+
+    var wrapper = document.getElementById('viewerContainer');
+    wrapper.classList.remove('presentationControls');
   },
 
   rotatePages: function pdfViewPageRotation(delta) {
 
     this.pageRotation = (this.pageRotation + 360 + delta) % 360;
 
     for (var i = 0, l = this.pages.length; i < l; i++) {
       var page = this.pages[i];
@@ -1221,41 +1576,107 @@ var PDFView = {
     this.parseScale(this.currentScaleValue, true);
 
     this.renderHighestPriority();
 
     // Wait for fullscreen to take effect
     setTimeout(function() {
       currentPage.scrollIntoView();
     }, 0);
+  },
+
+  /**
+   * This function flips the page in presentation mode if the user scrolls up
+   * or down with large enough motion and prevents page flipping too often.
+   *
+   * @this {PDFView}
+   * @param {number} mouseScrollDelta The delta value from the mouse event.
+   */
+  mouseScroll: function pdfViewMouseScroll(mouseScrollDelta) {
+    var MOUSE_SCROLL_COOLDOWN_TIME = 50;
+
+    var currentTime = (new Date()).getTime();
+    var storedTime = this.mouseScrollTimeStamp;
+
+    // In case one page has already been flipped there is a cooldown time
+    // which has to expire before next page can be scrolled on to.
+    if (currentTime > storedTime &&
+        currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME)
+      return;
+
+    // In case the user decides to scroll to the opposite direction than before
+    // clear the accumulated delta.
+    if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) ||
+        (this.mouseScrollDelta < 0 && mouseScrollDelta > 0))
+      this.clearMouseScrollState();
+
+    this.mouseScrollDelta += mouseScrollDelta;
+
+    var PAGE_FLIP_THRESHOLD = 120;
+    if (Math.abs(this.mouseScrollDelta) >= PAGE_FLIP_THRESHOLD) {
+
+      var PageFlipDirection = {
+        UP: -1,
+        DOWN: 1
+      };
+
+      // In fullscreen mode scroll one page at a time.
+      var pageFlipDirection = (this.mouseScrollDelta > 0) ?
+                                PageFlipDirection.UP :
+                                PageFlipDirection.DOWN;
+      this.clearMouseScrollState();
+      var currentPage = this.page;
+
+      // In case we are already on the first or the last page there is no need
+      // to do anything.
+      if ((currentPage == 1 && pageFlipDirection == PageFlipDirection.UP) ||
+          (currentPage == this.pages.length &&
+           pageFlipDirection == PageFlipDirection.DOWN))
+        return;
+
+      this.page += pageFlipDirection;
+      this.mouseScrollTimeStamp = currentTime;
+    }
+  },
+
+  /**
+   * This function clears the member attributes used with mouse scrolling in
+   * presentation mode.
+   *
+   * @this {PDFView}
+   */
+  clearMouseScrollState: function pdfViewClearMouseScrollState() {
+    this.mouseScrollTimeStamp = 0;
+    this.mouseScrollDelta = 0;
   }
 };
 
 var PageView = function pageView(container, pdfPage, id, scale,
                                  stats, navigateTo) {
   this.id = id;
   this.pdfPage = pdfPage;
 
   this.rotation = 0;
   this.scale = scale || 1.0;
   this.viewport = this.pdfPage.getViewport(this.scale, this.pdfPage.rotate);
 
   this.renderingState = RenderingStates.INITIAL;
   this.resume = null;
 
   this.textContent = null;
+  this.textLayer = null;
 
   var anchor = document.createElement('a');
   anchor.name = '' + this.id;
 
   var div = this.el = document.createElement('div');
   div.id = 'pageContainer' + this.id;
   div.className = 'page';
-  div.style.width = this.viewport.width + 'px';
-  div.style.height = this.viewport.height + 'px';
+  div.style.width = Math.floor(this.viewport.width) + 'px';
+  div.style.height = Math.floor(this.viewport.height) + 'px';
 
   container.appendChild(anchor);
   container.appendChild(div);
 
   this.destroy = function pageViewDestroy() {
     this.update();
     this.pdfPage.destroy();
   };
@@ -1269,18 +1690,18 @@ var PageView = function pageView(contain
     }
 
     this.scale = scale || this.scale;
 
     var totalRotation = (this.rotation + this.pdfPage.rotate) % 360;
     var viewport = this.pdfPage.getViewport(this.scale, totalRotation);
 
     this.viewport = viewport;
-    div.style.width = viewport.width + 'px';
-    div.style.height = viewport.height + 'px';
+    div.style.width = Math.floor(viewport.width) + 'px';
+    div.style.height = Math.floor(viewport.height) + 'px';
 
     while (div.hasChildNodes())
       div.removeChild(div.lastChild);
     div.removeAttribute('data-loaded');
 
     delete this.canvas;
 
     this.loadingIconDiv = document.createElement('div');
@@ -1483,21 +1904,22 @@ var PageView = function pageView(contain
     this.canvas = canvas;
 
     var textLayerDiv = null;
     if (!PDFJS.disableTextLayer) {
       textLayerDiv = document.createElement('div');
       textLayerDiv.className = 'textLayer';
       div.appendChild(textLayerDiv);
     }
-    var textLayer = textLayerDiv ? new TextLayerBuilder(textLayerDiv) : null;
+    var textLayer = this.textLayer =
+          textLayerDiv ? new TextLayerBuilder(textLayerDiv, this.id - 1) : null;
 
     var scale = this.scale, viewport = this.viewport;
-    canvas.width = viewport.width;
-    canvas.height = viewport.height;
+    canvas.width = Math.floor(viewport.width);
+    canvas.height = Math.floor(viewport.height);
 
     var ctx = canvas.getContext('2d');
     ctx.save();
     ctx.fillStyle = 'rgb(255, 255, 255)';
     ctx.fillRect(0, 0, canvas.width, canvas.height);
     ctx.restore();
 
     // Rendering area
@@ -1842,60 +2264,70 @@ var CustomStyle = (function CustomStyleC
     var prop = this.getProp(propName);
     if (prop != 'undefined')
       element.style[prop] = str;
   };
 
   return CustomStyle;
 })();
 
-var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
+var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
   var textLayerFrag = document.createDocumentFragment();
+
   this.textLayerDiv = textLayerDiv;
   this.layoutDone = false;
   this.divContentDone = false;
+  this.pageIdx = pageIdx;
+  this.matches = [];
 
   this.beginLayout = function textLayerBuilderBeginLayout() {
     this.textDivs = [];
     this.textLayerQueue = [];
+    this.renderingDone = false;
   };
 
   this.endLayout = function textLayerBuilderEndLayout() {
     this.layoutDone = true;
     this.insertDivContent();
-  },
+  };
 
   this.renderLayer = function textLayerBuilderRenderLayer() {
     var self = this;
     var textDivs = this.textDivs;
     var textLayerDiv = this.textLayerDiv;
     var canvas = document.createElement('canvas');
     var ctx = canvas.getContext('2d');
 
     // No point in rendering so many divs as it'd make the browser unusable
     // even after the divs are rendered
-    if (textDivs.length > 100000)
+    var MAX_TEXT_DIVS_TO_RENDER = 100000;
+    if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER)
       return;
 
-    while (textDivs.length > 0) {
-      var textDiv = textDivs.shift();
+    for (var i = 0, ii = textDivs.length; i < ii; i++) {
+      var textDiv = textDivs[i];
       textLayerFrag.appendChild(textDiv);
 
       ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
       var width = ctx.measureText(textDiv.textContent).width;
 
       if (width > 0) {
         var textScale = textDiv.dataset.canvasWidth / width;
 
         CustomStyle.setProp('transform' , textDiv,
           'scale(' + textScale + ', 1)');
         CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
+
+        textLayerDiv.appendChild(textDiv);
       }
     }
 
+    this.renderingDone = true;
+    this.updateMatches();
+
     textLayerDiv.appendChild(textLayerFrag);
   };
 
   this.setupRenderLayoutTimer = function textLayerSetupRenderLayoutTimer() {
     // Schedule renderLayout() if user has been scrolling, otherwise
     // run it right away
     var kRenderDelay = 200; // in ms
     var self = this;
@@ -1907,27 +2339,26 @@ var TextLayerBuilder = function textLaye
       if (this.renderTimer)
         clearTimeout(this.renderTimer);
       this.renderTimer = setTimeout(function() {
         self.setupRenderLayoutTimer();
       }, kRenderDelay);
     }
   };
 
-  this.appendText = function textLayerBuilderAppendText(fontName, fontSize,
-                                                        geom) {
+  this.appendText = function textLayerBuilderAppendText(geom) {
     var textDiv = document.createElement('div');
 
     // vScale and hScale already contain the scaling to pixel units
-    var fontHeight = fontSize * geom.vScale;
+    var fontHeight = geom.fontSize * geom.vScale;
     textDiv.dataset.canvasWidth = geom.canvasWidth * geom.hScale;
-    textDiv.dataset.fontName = fontName;
+    textDiv.dataset.fontName = geom.fontName;
 
     textDiv.style.fontSize = fontHeight + 'px';
-    textDiv.style.fontFamily = fontName;
+    textDiv.style.fontFamily = geom.fontFamily;
     textDiv.style.left = geom.x + 'px';
     textDiv.style.top = (geom.y - fontHeight) + 'px';
 
     // The content of the div is set in the `setTextContent` function.
 
     this.textDivs.push(textDiv);
   };
 
@@ -1952,16 +2383,211 @@ var TextLayerBuilder = function textLaye
 
     this.setupRenderLayoutTimer();
   };
 
   this.setTextContent = function textLayerBuilderSetTextContent(textContent) {
     this.textContent = textContent;
     this.insertDivContent();
   };
+
+  this.convertMatches = function textLayerBuilderConvertMatches(matches) {
+    var i = 0;
+    var iIndex = 0;
+    var bidiTexts = this.textContent.bidiTexts;
+    var end = bidiTexts.length - 1;
+    var queryLen = PDFFindController.state.query.length;
+
+    var lastDivIdx = -1;
+    var pos;
+
+    var ret = [];
+
+    // Loop over all the matches.
+    for (var m = 0; m < matches.length; m++) {
+      var matchIdx = matches[m];
+      // # Calculate the begin position.
+
+      // Loop over the divIdxs.
+      while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
+        iIndex += bidiTexts[i].str.length;
+        i++;
+      }
+
+      // TODO: Do proper handling here if something goes wrong.
+      if (i == bidiTexts.length) {
+        console.error('Could not find matching mapping');
+      }
+
+      var match = {
+        begin: {
+          divIdx: i,
+          offset: matchIdx - iIndex
+        }
+      };
+
+      // # Calculate the end position.
+      matchIdx += queryLen;
+
+      // Somewhat same array as above, but use a > instead of >= to get the end
+      // position right.
+      while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
+        iIndex += bidiTexts[i].str.length;
+        i++;
+      }
+
+      match.end = {
+        divIdx: i,
+        offset: matchIdx - iIndex
+      };
+      ret.push(match);
+    }
+
+    return ret;
+  };
+
+  this.renderMatches = function textLayerBuilder_renderMatches(matches) {
+    // Early exit if there is nothing to render.
+    if (matches.length === 0) {
+      return;
+    }
+
+    var bidiTexts = this.textContent.bidiTexts;
+    var textDivs = this.textDivs;
+    var prevEnd = null;
+    var isSelectedPage = this.pageIdx === PDFFindController.selected.pageIdx;
+    var selectedMatchIdx = PDFFindController.selected.matchIdx;
+    var highlightAll = PDFFindController.state.highlightAll;
+
+    var infty = {
+      divIdx: -1,
+      offset: undefined
+    };
+
+    function beginText(begin, className) {
+      var divIdx = begin.divIdx;
+      var div = textDivs[divIdx];
+      div.innerHTML = '';
+
+      var content = bidiTexts[divIdx].str.substring(0, begin.offset);
+      var node = document.createTextNode(content);
+      if (className) {
+        var isSelected = isSelectedPage &&
+                          divIdx === selectedMatchIdx;
+        var span = document.createElement('span');
+        span.className = className + (isSelected ? ' selected' : '');
+        span.appendChild(node);
+        div.appendChild(span);
+        return;
+      }
+      div.appendChild(node);
+    }
+
+    function appendText(from, to, className) {
+      var divIdx = from.divIdx;
+      var div = textDivs[divIdx];
+
+      var content = bidiTexts[divIdx].str.substring(from.offset, to.offset);
+      var node = document.createTextNode(content);
+      if (className) {
+        var span = document.createElement('span');
+        span.className = className;
+        span.appendChild(node);
+        div.appendChild(span);
+        return;
+      }
+      div.appendChild(node);
+    }
+
+    function highlightDiv(divIdx, className) {
+      textDivs[divIdx].className = className;
+    }
+
+    var i0 = selectedMatchIdx, i1 = i0 + 1, i;
+
+    if (highlightAll) {
+      i0 = 0;
+      i1 = matches.length;
+    } else if (!isSelectedPage) {
+      // Not highlighting all and this isn't the selected page, so do nothing.
+      return;
+    }
+
+    for (i = i0; i < i1; i++) {
+      var match = matches[i];
+      var begin = match.begin;
+      var end = match.end;
+
+      var isSelected = isSelectedPage && i === selectedMatchIdx;
+      var highlightSuffix = (isSelected ? ' selected' : '');
+      if (isSelected)
+        scrollIntoView(textDivs[begin.divIdx], {top: -50});
+
+      // Match inside new div.
+      if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
+        // If there was a previous div, then add the text at the end
+        if (prevEnd !== null) {
+          appendText(prevEnd, infty);
+        }
+        // clears the divs and set the content until the begin point.
+        beginText(begin);
+      } else {
+        appendText(prevEnd, begin);
+      }
+
+      if (begin.divIdx === end.divIdx) {
+        appendText(begin, end, 'highlight' + highlightSuffix);
+      } else {
+        appendText(begin, infty, 'highlight begin' + highlightSuffix);
+        for (var n = begin.divIdx + 1; n < end.divIdx; n++) {
+          highlightDiv(n, 'highlight middle' + highlightSuffix);
+        }
+        beginText(end, 'highlight end' + highlightSuffix);
+      }
+      prevEnd = end;
+    }
+
+    if (prevEnd) {
+      appendText(prevEnd, infty);
+    }
+  };
+
+  this.updateMatches = function textLayerUpdateMatches() {
+    // Only show matches, once all rendering is done.
+    if (!this.renderingDone)
+      return;
+
+    // Clear out all matches.
+    var matches = this.matches;
+    var textDivs = this.textDivs;
+    var bidiTexts = this.textContent.bidiTexts;
+    var clearedUntilDivIdx = -1;
+
+    // Clear out all current matches.
+    for (var i = 0; i < matches.length; i++) {
+      var match = matches[i];
+      var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
+      for (var n = begin; n <= match.end.divIdx; n++) {
+        var div = textDivs[n];
+        div.textContent = bidiTexts[n].str;
+        div.className = '';
+      }
+      clearedUntilDivIdx = match.end.divIdx + 1;
+    }
+
+    if (!PDFFindController.active)
+      return;
+
+    // Convert the matches on the page controller into the match format used
+    // for the textLayer.
+    this.matches = matches =
+      this.convertMatches(PDFFindController.pageMatches[this.pageIdx] || []);
+
+    this.renderMatches(this.matches);
+  };
 };
 
 document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
   PDFView.initialize();
   var params = PDFView.parseQueryString(document.location.search.substring(1));
 
   var file = window.location.toString()
 
@@ -1992,28 +2618,28 @@ document.addEventListener('DOMContentLoa
   if ('pdfBug' in hashParams && FirefoxCom.requestSync('pdfBugEnabled')) {
     PDFJS.pdfBug = true;
     var pdfBug = hashParams['pdfBug'];
     var enabled = pdfBug.split(',');
     PDFBug.enable(enabled);
     PDFBug.init();
   }
 
-  if (FirefoxCom.requestSync('searchEnabled')) {
-    document.querySelector('#viewSearch').classList.remove('hidden');
-  }
-
   if (!PDFView.supportsPrinting) {
     document.getElementById('print').classList.add('hidden');
   }
 
   if (!PDFView.supportsFullscreen) {
     document.getElementById('fullscreen').classList.add('hidden');
   }
 
+  if (PDFView.supportsIntegratedFind) {
+    document.querySelector('#viewFind').classList.add('hidden');
+  }
+
   // Listen for warnings to trigger the fallback UI.  Errors should be caught
   // and call PDFView.error() so we don't need to listen for those.
   PDFJS.LogManager.addLogger({
     warn: function() {
       PDFView.fallback();
     }
   });
 
@@ -2042,26 +2668,16 @@ document.addEventListener('DOMContentLoa
       PDFView.switchSidebarView('thumbs');
     });
 
   document.getElementById('viewOutline').addEventListener('click',
     function() {
       PDFView.switchSidebarView('outline');
     });
 
-  document.getElementById('viewSearch').addEventListener('click',
-    function() {
-      PDFView.switchSidebarView('search');
-    });
-
-  document.getElementById('searchButton').addEventListener('click',
-    function() {
-      PDFView.search();
-    });
-
   document.getElementById('previous').addEventListener('click',
     function() {
       PDFView.page--;
     });
 
   document.getElementById('next').addEventListener('click',
     function() {
       PDFView.page++;
@@ -2092,58 +2708,64 @@ document.addEventListener('DOMContentLoa
       window.print();
     });
 
   document.getElementById('download').addEventListener('click',
     function() {
       PDFView.download();
     });
 
-  document.getElementById('searchTermsInput').addEventListener('keydown',
-    function(event) {
-      if (event.keyCode == 13) {
-        PDFView.search();
-      }
-    });
-
   document.getElementById('pageNumber').addEventListener('change',
     function() {
       PDFView.page = this.value;
     });
 
   document.getElementById('scaleSelect').addEventListener('change',
     function() {
       PDFView.parseScale(this.value);
     });
 
+  document.getElementById('first_page').addEventListener('click',
+    function() {
+      PDFView.page = 1;
+    });
+
+  document.getElementById('last_page').addEventListener('click',
+    function() {
+      PDFView.page = PDFView.pdfDocument.numPages;
+    });
+
   document.getElementById('page_rotate_ccw').addEventListener('click',
-      function() {
-        PDFView.rotatePages(-90);
-      });
+    function() {
+      PDFView.rotatePages(-90);
+    });
 
   document.getElementById('page_rotate_cw').addEventListener('click',
-      function() {
-        PDFView.rotatePages(90);
-      });
+    function() {
+      PDFView.rotatePages(90);
+    });
 
   if (FirefoxCom.requestSync('getLoadingType') == 'passive') {
     PDFView.setTitleUsingUrl(file);
     PDFView.initPassiveLoading();
     return;
   }
 
   PDFView.open(file, 0);
 }, true);
 
 function updateViewarea() {
 
   if (!PDFView.initialized)
     return;
   var visible = PDFView.getVisiblePages();
   var visiblePages = visible.views;
+  if (visiblePages.length === 0) {
+    return;
+  }
 
   PDFView.renderHighestPriority();
 
   var currentId = PDFView.page;
   var firstPage = visible.first;
 
   for (var i = 0, ii = visiblePages.length, stillFullyVisible = false;
        i < ii; ++i) {
@@ -2177,21 +2799,23 @@ function updateViewarea() {
   var pdfOpenParams = '#page=' + pageNumber;
   pdfOpenParams += '&zoom=' + normalizedScaleValue;
   var currentPage = PDFView.pages[pageNumber - 1];
   var topLeft = currentPage.getPagePoint(PDFView.container.scrollLeft,
     (PDFView.container.scrollTop - firstPage.y));
   pdfOpenParams += ',' + Math.round(topLeft[0]) + ',' + Math.round(topLeft[1]);
 
   var store = PDFView.store;
-  store.set('exists', true);
-  store.set('page', pageNumber);
-  store.set('zoom', normalizedScaleValue);
-  store.set('scrollLeft', Math.round(topLeft[0]));
-  store.set('scrollTop', Math.round(topLeft[1]));
+  store.initializedPromise.then(function() {
+    store.set('exists', true);
+    store.set('page', pageNumber);
+    store.set('zoom', normalizedScaleValue);
+    store.set('scrollLeft', Math.round(topLeft[0]));
+    store.set('scrollTop', Math.round(topLeft[1]));
+  });
   var href = PDFView.getAnchorUrl(pdfOpenParams);
   document.getElementById('viewBookmark').href = href;
 }
 
 window.addEventListener('resize', function webViewerResize(evt) {
   if (PDFView.initialized &&
       (document.getElementById('pageWidthOption').selected ||
       document.getElementById('pageFitOption').selected ||
@@ -2296,30 +2920,54 @@ window.addEventListener('pagechange', fu
 window.addEventListener('DOMMouseScroll', function(evt) {
   if (evt.ctrlKey) {
     evt.preventDefault();
 
     var ticks = evt.detail;
     var direction = (ticks > 0) ? 'zoomOut' : 'zoomIn';
     for (var i = 0, length = Math.abs(ticks); i < length; i++)
       PDFView[direction]();
+  } else if (PDFView.isFullscreen) {
+    var FIREFOX_DELTA_FACTOR = -40;
+    PDFView.mouseScroll(evt.detail * FIREFOX_DELTA_FACTOR);
+  }
+}, false);
+
+window.addEventListener('mousemove', function keydown(evt) {
+  if (PDFView.isFullscreen) {
+    PDFView.showPresentationControls();
+  }
+}, false);
+
+window.addEventListener('mousedown', function mousedown(evt) {
+  if (PDFView.isFullscreen && evt.button === 0) {
+    // Mouse click in fullmode advances a page
+    evt.preventDefault();
+
+    PDFView.page++;
   }
 }, false);
 
 window.addEventListener('keydown', function keydown(evt) {
   var handled = false;
   var cmd = (evt.ctrlKey ? 1 : 0) |
             (evt.altKey ? 2 : 0) |
             (evt.shiftKey ? 4 : 0) |
             (evt.metaKey ? 8 : 0);
 
   // First, handle the key bindings that are independent whether an input
   // control is selected or not.
   if (cmd == 1 || cmd == 8) { // either CTRL or META key.
     switch (evt.keyCode) {
+      case 70:
+        if (!PDFView.supportsIntegratedFind) {
+          PDFFindBar.toggle();
+          handled = true;
+        }
+        break;
       case 61: // FF/Mac '='
       case 107: // FF '+' and '='
       case 187: // Chrome '+'
         PDFView.zoomIn();
         handled = true;
         break;
       case 173: // FF/Mac '-'
       case 109: // FF '-'
@@ -2329,51 +2977,85 @@ window.addEventListener('keydown', funct
         break;
       case 48: // '0'
         PDFView.parseScale(kDefaultScale, true);
         handled = true;
         break;
     }
   }
 
+  // CTRL or META with or without SHIFT.
+  if (cmd == 1 || cmd == 8 || cmd == 5 || cmd == 12) {
+    switch (evt.keyCode) {
+      case 71: // g
+        if (!PDFView.supportsIntegratedFind) {
+          PDFFindBar.dispatchEvent('again', cmd == 5 || cmd == 12);
+          handled = true;
+        }
+        break;
+    }
+  }
+
   if (handled) {
     evt.preventDefault();
     return;
   }
 
   // Some shortcuts should not get handled if a control/input element
   // is selected.
   var curElement = document.activeElement;
-  if (curElement && curElement.tagName == 'INPUT')
+  if (curElement && (curElement.tagName == 'INPUT' ||
+                     curElement.tagName == 'SELECT')) {
     return;
-  var controlsElement = document.getElementById('controls');
+  }
+  var controlsElement = document.getElementById('toolbar');
   while (curElement) {
     if (curElement === controlsElement && !PDFView.isFullscreen)
-      return; // ignoring if the 'controls' element is focused
+      return; // ignoring if the 'toolbar' element is focused
     curElement = curElement.parentNode;
   }
 
   if (cmd == 0) { // no control key pressed at all.
     switch (evt.keyCode) {
+      case 38: // up arrow
+      case 33: // pg up
+      case 8: // backspace
+        if (!PDFView.isFullscreen) {
+          break;
+        }
+        //  in fullscreen mode falls throw here
       case 37: // left arrow
       case 75: // 'k'
       case 80: // 'p'
         PDFView.page--;
         handled = true;
         break;
+      case 40: // down arrow
+      case 34: // pg down
+      case 32: // spacebar
+        if (!PDFView.isFullscreen) {
+          break;
+        }
+        //  in fullscreen mode falls throw here
       case 39: // right arrow
       case 74: // 'j'
       case 78: // 'n'
         PDFView.page++;
         handled = true;
         break;
 
-      case 32: // spacebar
+      case 36: // home
         if (PDFView.isFullscreen) {
-          PDFView.page++;
+          PDFView.page = 1;
+          handled = true;
+        }
+        break;
+      case 35: // end
+        if (PDFView.isFullscreen) {
+          PDFView.page = PDFView.pdfDocument.numPages;
           handled = true;
         }
         break;
 
       case 82: // 'r'
         PDFView.rotatePages(90);
         break;
     }
@@ -2384,16 +3066,17 @@ window.addEventListener('keydown', funct
       case 82: // 'r'
         PDFView.rotatePages(-90);
         break;
     }
   }
 
   if (handled) {
     evt.preventDefault();
+    PDFView.clearMouseScrollState();
   }
 });
 
 window.addEventListener('beforeprint', function beforePrint(evt) {
   PDFView.beforePrint();
 });
 
 window.addEventListener('afterprint', function afterPrint(evt) {
--- a/browser/extensions/pdfjs/extension-files
+++ b/browser/extensions/pdfjs/extension-files
@@ -1,16 +1,21 @@
 chrome.manifest
 components/PdfStreamConverter.js
 content/PdfJs.jsm
 content/web/debugger.js
 content/web/images/annotation-check.svg
 content/web/images/annotation-comment.svg
 content/web/images/annotation-text.svg
+content/web/images/findbarButton-next-rtl.png
+content/web/images/findbarButton-next.png
+content/web/images/findbarButton-previous-rtl.png
+content/web/images/findbarButton-previous.png
 content/web/images/loading-icon.gif
+content/web/images/loading-small.png
 content/web/images/texture.png
 content/web/images/toolbarButton-bookmark.png
 content/web/images/toolbarButton-download.png
 content/web/images/toolbarButton-fullscreen.png
 content/web/images/toolbarButton-menuArrows.png
 content/web/images/toolbarButton-openFile.png
 content/web/images/toolbarButton-pageDown-rtl.png
 content/web/images/toolbarButton-pageDown.png
--- a/browser/locales/en-US/pdfviewer/chrome.properties
+++ b/browser/locales/en-US/pdfviewer/chrome.properties
@@ -1,8 +1,4 @@
-# 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/.
-
 # Chrome notification bar messages and buttons
 unsupported_feature=This PDF document might not be displayed correctly.
 open_with_different_viewer=Open With Different Viewer
 open_with_different_viewer.accessKey=o
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -1,12 +1,8 @@
-# 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/.
-
 # Main toolbar buttons (tooltips and alt text for images)
 previous.title=Previous Page
 previous_label=Previous
 next.title=Next Page
 next_label=Next
 
 # LOCALIZATION NOTE (page_label, page_of):
 # These strings are concatenated to form the "Page: X of Y" string.
@@ -35,37 +31,47 @@ bookmark_label=Current View
 # (the _label strings are alt text for the buttons, the .title strings are
 # tooltips)
 toggle_slider.title=Toggle Slider
 toggle_slider_label=Toggle Slider
 outline.title=Show Document Outline
 outline_label=Document Outline
 thumbs.title=Show Thumbnails
 thumbs_label=Thumbnails
-search_panel.title=Search Document
-search_panel_label=Search
+findbar.title=Find in Document
+findbar_label=Find
 
 # Document outline messages
 no_outline=No Outline Available
 
 # Thumbnails panel item (tooltip and alt text for images)
 # LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page
 # number.
 thumb_page_title=Page {{page}}
 # LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page
 # number.
 thumb_page_canvas=Thumbnail of Page {{page}}
 
 # Context menu
+first_page.label=Go to First Page
+last_page.label=Go to Last Page
 page_rotate_cw.label=Rotate Clockwise
-page_rotate_ccw.label=Rotate Counter-Clockwise
+page_rotate_ccw.label=Rotate Counterclockwise
 
-# Search panel button title and messages
-search=Find
-search_terms_not_found=(Not found)
+# Find panel button title and messages
+find_label=Find:
+find_previous.title=Find the previous occurrence of the phrase
+find_previous_label=Previous
+find_next.title=Find the next occurrence of the phrase
+find_next_label=Next
+find_highlight=Highlight all
+find_match_case_label=Match case
+find_wrapped_to_bottom=Reached end of page, continued from bottom
+find_wrapped_to_top=Reached end of page, continued from top
+find_not_found=Phrase not found
 
 # Error panel labels
 error_more_info=More Information
 error_less_info=Less Information
 error_close=Close
 # LOCALIZATION NOTE (error_build): "{{build}}" will be replaced by the PDF.JS
 # build ID.
 error_build=PDF.JS Build: {{build}}
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -4106,17 +4106,16 @@ html|*#gcli-output-frame {
   margin: 4px;
   -moz-margin-start: 0;
 }
 
 .social-panel > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
 
-#social-notification-box,
 .social-panel-frame {
   border-radius: inherit;
 }
 
 /* === end of social toolbar provider menu === */
 
 .chat-status-icon {
   max-height: 16px;
--- a/browser/themes/pinstripe/downloads/downloads.css
+++ b/browser/themes/pinstripe/downloads/downloads.css
@@ -148,17 +148,16 @@ richlistitem[type="download"][state="1"]
 }
 
 .downloadButton.downloadRetry {
   -moz-image-region: rect(32px, 16px, 48px, 0px);
 }
 
 /*** Status and progress indicator ***/
 
-#downloads-button,
 #downloads-indicator {
   width: 35px;
 }
 
 #downloads-indicator-anchor {
   min-width: 20px;
   min-height: 20px;
   /* Makes the outermost stack element positioned, so that its contents are
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -3390,17 +3390,16 @@ html|*#gcli-output-frame {
 #social-statusarea-user[_moz-menuactive] > vbox > #social-statusarea-username {
   text-decoration: underline;
 }
 
 .social-panel > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
 
-#social-notification-box,
 .social-panel-frame {
   border-radius: inherit;
 }
 
 .chat-status-icon {
   max-height: 16px;
   max-width: 16px;
   padding: 0;
--- a/build/macosx/universal/mozconfig.common
+++ b/build/macosx/universal/mozconfig.common
@@ -1,19 +1,19 @@
 # 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/.
 
 mk_add_options MOZ_UNIFY_BDATE=1
 
 mk_add_options MOZ_POSTFLIGHT_ALL+=build/macosx/universal/flight.mk
 
-# Note, the version (10) is used by libffi's configure.
-ac_add_app_options i386 --target=i386-apple-darwin10
-ac_add_app_options x86_64 --target=x86_64-apple-darwin10
+DARWIN_VERSION=`uname -r`
+ac_add_app_options i386 --target=i386-apple-darwin$DARWIN_VERSION
+ac_add_app_options x86_64 --target=x86_64-apple-darwin$DARWIN_VERSION
 
 ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.6.sdk
 
 . $topsrcdir/build/macosx/mozconfig.common
 
 # $MOZ_BUILD_APP is only defined when sourced by configure.  That's not a
 # problem, because the variables it affects only need to be set for
 # configure.
--- a/config/system-headers
+++ b/config/system-headers
@@ -476,16 +476,17 @@ mach/mach_interface.h
 mach/mach_port.h
 mach-o/dyld.h
 MacLocales.h
 MacMemory.h
 MacTCP.h
 MacTypes.h
 MacWindows.h
 malloc.h
+malloc_np.h
 map
 mapicode.h
 mapidefs.h
 mapiguid.h
 mapi.h
 mapitags.h
 mapiutil.h
 mapix.h
--- a/configure.in
+++ b/configure.in
@@ -70,17 +70,17 @@ GTK2_VERSION=2.10.0
 WINDRES_VERSION=2.14.90
 W32API_VERSION=3.14
 GNOMEVFS_VERSION=2.0
 GNOMEUI_VERSION=2.2.0
 GCONF_VERSION=1.2.1
 GIO_VERSION=2.18
 STARTUP_NOTIFICATION_VERSION=0.8
 DBUS_VERSION=0.60
-SQLITE_VERSION=3.7.14
+SQLITE_VERSION=3.7.14.1
 
 MSMANIFEST_TOOL=
 
 dnl Set various checks
 dnl ========================================================
 MISSING_X=
 AC_PROG_AWK
 
@@ -3763,31 +3763,32 @@ if test -n "$MOZ_LINKER" -a -z "$MOZ_OLD
     dnl libraries there.
     DSO_LDOPTS="$DSO_LDOPTS -nostartfiles"
     NSPR_LDFLAGS=-nostartfiles
   fi
 fi
 
 dnl Check for the existence of various allocation headers/functions
 
+MALLOC_HEADERS="malloc.h malloc_np.h malloc/malloc.h sys/malloc.h"
 MALLOC_H=
-MOZ_CHECK_HEADER(malloc.h,        [MALLOC_H=malloc.h])
-if test "$MALLOC_H" = ""; then
-  MOZ_CHECK_HEADER(malloc/malloc.h, [MALLOC_H=malloc/malloc.h])
-  if test "$MALLOC_H" = ""; then
-    MOZ_CHECK_HEADER(sys/malloc.h,    [MALLOC_H=sys/malloc.h])
+
+for file in $MALLOC_HEADERS; do
+  MOZ_CHECK_HEADER($file, [MALLOC_H=$file])
+  if test "$MALLOC_H" != ""; then
+    AC_DEFINE_UNQUOTED(MALLOC_H, <$MALLOC_H>)
+    break
   fi
-fi
-if test "$MALLOC_H" != ""; then
-   AC_DEFINE_UNQUOTED(MALLOC_H, <$MALLOC_H>)
-fi
+done
 
 MOZ_ALLOCATING_FUNCS="strndup posix_memalign memalign valloc"
 AC_CHECK_FUNCS(strndup posix_memalign memalign valloc)
 
+AC_CHECK_FUNCS(malloc_usable_size)
+
 dnl See if compiler supports some gcc-style attributes
 
 AC_CACHE_CHECK(for __attribute__((always_inline)),
                ac_cv_attribute_always_inline,
                [AC_TRY_COMPILE([inline void f(void) __attribute__((always_inline));],
                                [],
                                ac_cv_attribute_always_inline=yes,
                                ac_cv_attribute_always_inline=no)])
@@ -7020,16 +7021,28 @@ else
     MOZ_GLUE_PROGRAM_LDFLAGS="$MOZ_GLUE_PROGRAM_LDFLAGS -rdynamic"
   fi
   if test "$MOZ_LINKER" = 1; then
     MOZ_GLUE_PROGRAM_LDFLAGS="$MOZ_GLUE_PROGRAM_LDFLAGS $ZLIB_LIBS"
   fi
 fi
 
 if test -z "$MOZ_MEMORY"; then
+  if test -n "$MOZ_JEMALLOC"; then
+    MOZ_NATIVE_JEMALLOC=1
+    AC_CHECK_FUNCS(mallctl nallocm,,
+      [MOZ_NATIVE_JEMALLOC=
+       break])
+    if test -n "$MOZ_NATIVE_JEMALLOC"; then
+      MOZ_MEMORY=1
+      AC_DEFINE(MOZ_MEMORY)
+      AC_DEFINE(MOZ_JEMALLOC)
+      AC_DEFINE(MOZ_NATIVE_JEMALLOC)
+    fi
+  fi
   case "${target}" in
     *-mingw*)
       if test -z "$WIN32_REDIST_DIR" -a -z "$MOZ_DEBUG"; then
         AC_MSG_WARN([When not building jemalloc, you need to set WIN32_REDIST_DIR to the path to the Visual C++ Redist (usually VCINSTALLDIR\redist\x86\Microsoft.VC80.CRT, for VC++ v8) if you intend to distribute your build.])
       fi
       ;;
   esac
 else
@@ -7114,16 +7127,17 @@ else
     ;;
   *)
     AC_MSG_ERROR([--enable-jemalloc not supported on ${target}])
     ;;
   esac
 fi # MOZ_MEMORY
 AC_SUBST(MOZ_MEMORY)
 AC_SUBST(MOZ_JEMALLOC)
+AC_SUBST(MOZ_NATIVE_JEMALLOC)
 AC_SUBST(MOZ_GLUE_LDFLAGS)
 AC_SUBST(MOZ_GLUE_PROGRAM_LDFLAGS)
 AC_SUBST(WIN32_CRT_LIBS)
 dnl Need to set this for make because NSS doesn't have configure
 AC_SUBST(DLLFLAGS)
 
 dnl We need to wrap dlopen and related functions on Android because we use
 dnl our own linker.
@@ -8947,20 +8961,32 @@ MOZ_CRASHREPORTER=${MOZ_CRASHREPORTER} \
 if cmp -s ./mozinfo.json.tmp ./mozinfo.json; then
   rm ./mozinfo.json.tmp
 else
   mv -f ./mozinfo.json.tmp ./mozinfo.json
 fi
 
 # Run jemalloc configure script
 
-if test "$MOZ_JEMALLOC" -a "$MOZ_MEMORY"; then
+if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_JEMALLOC" -a "$MOZ_MEMORY" ; then
   ac_configure_args="$_SUBDIR_CONFIG_ARGS --build=$build --host=$target --enable-stats --with-jemalloc-prefix=je_"
-  if test "$OS_ARCH" = "Linux"; then
-    MANGLE="malloc calloc valloc free realloc memalign posix_memalign malloc_usable_size"
+  case "$OS_ARCH" in
+    Linux|DragonFly|FreeBSD|NetBSD|OpenBSD)
+      MANGLE="malloc calloc valloc free realloc posix_memalign"
+      case "$OS_ARCH" in
+        Linux)
+          MANGLE="$MANGLE memalign malloc_usable_size"
+          ;;
+        FreeBSD)
+          MANGLE="$MANGLE malloc_usable_size"
+          ;;
+      esac
+      ;;
+  esac
+  if test -n "$MANGLE"; then
     MANGLED=
     JEMALLOC_WRAPPER=
     if test -n "$_WRAP_MALLOC"; then
       JEMALLOC_WRAPPER=__wrap_
     fi
     for mangle in ${MANGLE}; do
       if test -n "$MANGLED"; then
         MANGLED="$mangle:$JEMALLOC_WRAPPER$mangle,$MANGLED"
--- a/content/base/public/FragmentOrElement.h
+++ b/content/base/public/FragmentOrElement.h
@@ -25,17 +25,17 @@
 
 class ContentUnbinder;
 class nsContentList;
 class nsDOMAttributeMap;
 class nsDOMTokenList;
 class nsIControllers;
 class nsICSSDeclaration;
 class nsIDocument;
-class nsIDOMDOMStringMap;
+class nsDOMStringMap;
 class nsIDOMNamedNodeMap;
 class nsINodeInfo;
 class nsIURI;
 
 /**
  * Class that implements the nsIDOMNodeList interface (a list of children of
  * the content), by holding a reference to the content and delegating GetLength
  * and Item to its existing child list.
@@ -343,17 +343,17 @@ public:
      * @see nsGenericHTMLElement::GetStyle
      */
     nsCOMPtr<nsICSSDeclaration> mStyle;
 
     /**
      * The .dataset attribute.
      * @see nsGenericHTMLElement::GetDataset
      */
-    nsIDOMDOMStringMap* mDataset; // [Weak]
+    nsDOMStringMap* mDataset; // [Weak]
 
     /**
      * SMIL Overridde style rules (for SMIL animation of CSS properties)
      * @see nsIContent::GetSMILOverrideStyle
      */
     nsCOMPtr<nsICSSDeclaration> mSMILOverrideStyle;
 
     /**
--- a/content/base/public/nsIObjectLoadingContent.idl
+++ b/content/base/public/nsIObjectLoadingContent.idl
@@ -44,16 +44,18 @@ interface nsIObjectLoadingContent : nsIS
   // The plugin is considered outdated, but not disabled
   const unsigned long PLUGIN_OUTDATED             = 4;
   // The plugin has crashed
   const unsigned long PLUGIN_CRASHED              = 5;
   // Suppressed by security policy
   const unsigned long PLUGIN_SUPPRESSED           = 6;
   // Blocked by content policy
   const unsigned long PLUGIN_USER_DISABLED        = 7;
+  /// ** All values >= PLUGIN_CLICK_TO_PLAY are plugin placeholder types that
+  ///    would be replaced by a real plugin if activated (playPlugin())
   // The plugin is disabled until the user clicks on it
   const unsigned long PLUGIN_CLICK_TO_PLAY        = 8;
   // The plugin is vulnerable (update available)
   const unsigned long PLUGIN_VULNERABLE_UPDATABLE = 9;
   // The plugin is vulnerable (no update available)
   const unsigned long PLUGIN_VULNERABLE_NO_UPDATE = 10;
   // The plugin is in play preview mode
   const unsigned long PLUGIN_PLAY_PREVIEW         = 11;
@@ -105,23 +107,24 @@ interface nsIObjectLoadingContent : nsIS
 
   [noscript] void pluginCrashed(in nsIPluginTag pluginTag,
                                 in AString pluginDumpID,
                                 in AString browserDumpID,
                                 in boolean submittedCrashReport);
 
   /**
    * This method will play a plugin that has been stopped by the
-   * click-to-play plugins feature.
+   * click-to-play plugins or play-preview features.
    */
   void playPlugin();
 
   /**
-   * This attribute will return true if the plugin has been activated and
-   * false if the plugin is still in the click-to-play or play preview state.
+   * This attribute will return true if the current content type has been
+   * activated, either explicitly or by passing checks that would have it be
+   * click-to-play or play-preview.
    */
   readonly attribute boolean activated;
 
   [noscript] void stopPluginInstance();
 
   [noscript] void syncStartPluginInstance();
   [noscript] void asyncStartPluginInstance();
 
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -1232,17 +1232,17 @@ FragmentOrElement::MarkUserDataHandler(v
 void
 FragmentOrElement::MarkNodeChildren(nsINode* aNode)
 {
   JSObject* o = GetJSObjectChild(aNode);
   xpc_UnmarkGrayObject(o);
 
   nsEventListenerManager* elm = aNode->GetListenerManager(false);
   if (elm) {
-    elm->UnmarkGrayJSListeners();
+    elm->MarkForCC();
   }
 
   if (aNode->HasProperties()) {
     nsIDocument* ownerDoc = aNode->OwnerDoc();
     ownerDoc->PropertyTable(DOM_USER_DATA)->
       Enumerate(aNode, FragmentOrElement::MarkUserData,
                 &nsCCUncollectableMarker::sGeneration);
     ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)->
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -182,16 +182,17 @@ LOCAL_INCLUDES += \
   -I$(topsrcdir)/content/xml/content/src \
   -I$(topsrcdir)/content/xml/document/src \
   -I$(topsrcdir)/content/xul/content/src \
   -I$(topsrcdir)/content/xul/document/src \
   -I$(topsrcdir)/dom/base \
   -I$(topsrcdir)/dom/ipc \
   -I$(topsrcdir)/image/src \
   -I$(topsrcdir)/js/xpconnect/src \
+  -I$(topsrcdir)/layout/base \
   -I$(topsrcdir)/layout/generic \
   -I$(topsrcdir)/layout/style \
   -I$(topsrcdir)/layout/svg \
   -I$(topsrcdir)/layout/xul/base/src \
   -I$(topsrcdir)/netwerk/base/src \
   $(NULL)
 
 DEFINES += -D_IMPL_NS_LAYOUT
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -580,17 +580,17 @@ WebSocket::Constructor(JSContext* aCx,
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(WebSocket)
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(WebSocket)
   bool isBlack = tmp->IsBlack();
   if (isBlack|| tmp->mKeepingAlive) {
     if (tmp->mListenerManager) {
-      tmp->mListenerManager->UnmarkGrayJSListeners();
+      tmp->mListenerManager->MarkForCC();
     }
     if (!isBlack && tmp->PreservingWrapper()) {
       xpc_UnmarkGrayObject(tmp->GetWrapperPreserveColor());
     }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
--- a/content/base/src/nsCCUncollectableMarker.cpp
+++ b/content/base/src/nsCCUncollectableMarker.cpp
@@ -87,61 +87,91 @@ MarkUserDataHandler(void* aNode, nsIAtom
   if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) {
     nsGenericElement::MarkUserDataHandler(aNode, aKey, aValue, aData);
   }
 }
 
 static void
 MarkMessageManagers()
 {
-  nsCOMPtr<nsIMessageBroadcaster> globalMM =
+  nsCOMPtr<nsIMessageBroadcaster> strongGlobalMM =
     do_GetService("@mozilla.org/globalmessagemanager;1");
-  if (!globalMM) {
+  if (!strongGlobalMM) {
     return;
   }
+  nsIMessageBroadcaster* globalMM = strongGlobalMM;
+  strongGlobalMM = nullptr;
 
   globalMM->MarkForCC();
   uint32_t childCount = 0;
   globalMM->GetChildCount(&childCount);
   for (uint32_t i = 0; i < childCount; ++i) {
     nsCOMPtr<nsIMessageListenerManager> childMM;
     globalMM->GetChildAt(i, getter_AddRefs(childMM));
     if (!childMM) {
       continue;
     }
-    nsCOMPtr<nsIMessageBroadcaster> windowMM = do_QueryInterface(childMM);
+    nsCOMPtr<nsIMessageBroadcaster> strongWindowMM = do_QueryInterface(childMM);
+    nsIMessageBroadcaster* windowMM = strongWindowMM;
+    childMM = nullptr;
+    strongWindowMM = nullptr;
     windowMM->MarkForCC();
     uint32_t tabChildCount = 0;
     windowMM->GetChildCount(&tabChildCount);
     for (uint32_t j = 0; j < tabChildCount; ++j) {
       nsCOMPtr<nsIMessageListenerManager> childMM;
       windowMM->GetChildAt(j, getter_AddRefs(childMM));
       if (!childMM) {
         continue;
       }
-      nsCOMPtr<nsIMessageSender> tabMM = do_QueryInterface(childMM);
+      nsCOMPtr<nsIMessageSender> strongTabMM = do_QueryInterface(childMM);
+      nsIMessageSender* tabMM = strongTabMM;
+      childMM = nullptr;
+      strongTabMM = nullptr;
       tabMM->MarkForCC();
       //XXX hack warning, but works, since we know that
       //    callback is frameloader.
       mozilla::dom::ipc::MessageManagerCallback* cb =
-        static_cast<nsFrameMessageManager*>(tabMM.get())->GetCallback();
+        static_cast<nsFrameMessageManager*>(tabMM)->GetCallback();
       if (cb) {
         nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
         nsIDOMEventTarget* et = fl->GetTabChildGlobalAsEventTarget();
         if (!et) {
           continue;
         }
         static_cast<nsInProcessTabChildGlobal*>(et)->MarkForCC();
         nsEventListenerManager* elm = et->GetListenerManager(false);
         if (elm) {
-          elm->UnmarkGrayJSListeners();
+          elm->MarkForCC();
         }
       }
     }
   }
+  if (nsFrameMessageManager::sParentProcessManager) {
+    nsFrameMessageManager::sParentProcessManager->MarkForCC();
+    uint32_t childCount = 0;
+    nsFrameMessageManager::sParentProcessManager->GetChildCount(&childCount);
+    for (uint32_t i = 0; i < childCount; ++i) {
+      nsCOMPtr<nsIMessageListenerManager> childMM;
+      nsFrameMessageManager::sParentProcessManager->
+        GetChildAt(i, getter_AddRefs(childMM));
+      if (!childMM) {
+        continue;
+      }
+      nsIMessageListenerManager* child = childMM;
+      childMM = nullptr;
+      child->MarkForCC();
+    }
+  }
+  if (nsFrameMessageManager::sSameProcessParentManager) {
+    nsFrameMessageManager::sSameProcessParentManager->MarkForCC();
+  }
+  if (nsFrameMessageManager::sChildProcessManager) {
+    nsFrameMessageManager::sChildProcessManager->MarkForCC();
+  }
 }
 
 void
 MarkContentViewer(nsIContentViewer* aViewer, bool aCleanupJS,
                   bool aPrepareForCC)
 {
   if (!aViewer) {
     return;
@@ -149,23 +179,23 @@ MarkContentViewer(nsIContentViewer* aVie
 
   nsIDocument *doc = aViewer->GetDocument();
   if (doc &&
       doc->GetMarkedCCGeneration() != nsCCUncollectableMarker::sGeneration) {
     doc->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
     if (aCleanupJS) {
       nsEventListenerManager* elm = doc->GetListenerManager(false);
       if (elm) {
-        elm->UnmarkGrayJSListeners();
+        elm->MarkForCC();
       }
       nsCOMPtr<nsIDOMEventTarget> win = do_QueryInterface(doc->GetInnerWindow());
       if (win) {
         elm = win->GetListenerManager(false);
         if (elm) {
-          elm->UnmarkGrayJSListeners();
+          elm->MarkForCC();
         }
         static_cast<nsGlobalWindow*>(win.get())->UnmarkGrayTimers();
       }
 
       doc->PropertyTable(DOM_USER_DATA_HANDLER)->
         EnumerateAll(MarkUserDataHandler, &nsCCUncollectableMarker::sGeneration);
     } else if (aPrepareForCC) {
       // Unfortunately we need to still mark user data just before running CC so
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -3913,17 +3913,17 @@ ListenerEnumerator(PLDHashTable* aTable,
                    uint32_t aNumber, void* aArg)
 {
   EventListenerManagerMapEntry* entry =
     static_cast<EventListenerManagerMapEntry*>(aEntry);
   if (entry) {
     nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
     if (n && n->IsInDoc() &&
         nsCCUncollectableMarker::InGeneration(n->OwnerDoc()->GetMarkedCCGeneration())) {
-      entry->mListenerManager->UnmarkGrayJSListeners();
+      entry->mListenerManager->MarkForCC();
     }
   }
   return PL_DHASH_NEXT;
 }
 
 void
 nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(uint32_t aGeneration)
 {
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -67,16 +67,17 @@
 #include "nsIJSON.h"
 #include "nsThreadUtils.h"
 #include "nsNodeInfoManager.h"
 #include "nsIFileChannel.h"
 #include "nsIMultiPartChannel.h"
 #include "nsIRefreshURI.h"
 #include "nsIWebNavigation.h"
 #include "nsIScriptError.h"
+#include "nsStyleSheetService.h"
 
 #include "nsNetUtil.h"     // for NS_MakeAbsoluteURI
 
 #include "nsIScriptSecurityManager.h"
 #include "nsIPrincipal.h"
 
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
@@ -1712,17 +1713,17 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(nsDocument,
                                               nsNodeUtils::LastRelease(this))
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
   if (nsGenericElement::CanSkip(tmp, aRemovingAllowed)) {
     nsEventListenerManager* elm = tmp->GetListenerManager(false);
     if (elm) {
-      elm->UnmarkGrayJSListeners();
+      elm->MarkForCC();
     }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument)
   return nsGenericElement::CanSkipInCC(tmp);
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
@@ -2313,16 +2314,24 @@ nsDocument::ResetStylesheetsToURI(nsIURI
   nsCOMPtr<nsIPresShell> shell = GetShell();
   if (shell) {
     FillStyleSet(shell->StyleSet());
   }
 
   return NS_OK;
 }
 
+static bool
+AppendAuthorSheet(nsIStyleSheet *aSheet, void *aData)
+{
+  nsStyleSet *styleSet = static_cast<nsStyleSet*>(aData);
+  styleSet->AppendStyleSheet(nsStyleSet::eDocSheet, aSheet);
+  return true;
+}
+
 void
 nsDocument::FillStyleSet(nsStyleSet* aStyleSet)
 {
   NS_PRECONDITION(aStyleSet, "Must have a style set");
   NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::ePresHintSheet) == 0,
                   "Style set already has a preshint sheet?");
   NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eDocSheet) == 0,
                   "Style set already has document sheets?");
@@ -2339,16 +2348,22 @@ nsDocument::FillStyleSet(nsStyleSet* aSt
   int32_t i;
   for (i = mStyleSheets.Count() - 1; i >= 0; --i) {
     nsIStyleSheet* sheet = mStyleSheets[i];
     if (sheet->IsApplicable()) {
       aStyleSet->AddDocStyleSheet(sheet, this);
     }
   }
 
+  nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
+  if (sheetService) {
+    sheetService->AuthorStyleSheets()->EnumerateForwards(AppendAuthorSheet,
+                                                         aStyleSet);
+  }
+
   for (i = mCatalogSheets.Count() - 1; i >= 0; --i) {
     nsIStyleSheet* sheet = mCatalogSheets[i];
     if (sheet->IsApplicable()) {
       aStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet);
     }
   }
 
   for (int32_t i = mAdditionalSheets[eAgentSheet].Count() - 1; i >= 0; --i) {
@@ -3578,24 +3593,16 @@ nsDocument::GetStyleSheetAt(int32_t aInd
 {
   NS_ENSURE_TRUE(0 <= aIndex && aIndex < mStyleSheets.Count(), nullptr);
   return mStyleSheets[aIndex];
 }
 
 int32_t
 nsDocument::GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const
 {
-  if (mAdditionalSheets[eUserSheet].IndexOf(aSheet) >= 0 ||
-      mAdditionalSheets[eAgentSheet].IndexOf(aSheet) >= 0 ) {
-    // Returning INT32_MAX to make sure that additional sheets are
-    // in the style set of the PresShell will be always after the
-    // document sheets (even if document sheets are added dynamically).
-    return INT32_MAX;
-  }
-
   return mStyleSheets.IndexOf(aSheet);
 }
 
 void
 nsDocument::AddStyleSheetToStyleSets(nsIStyleSheet* aSheet)
 {
   nsCOMPtr<nsIPresShell> shell = GetShell();
   if (shell) {
@@ -10053,16 +10060,33 @@ nsDocument::DocSizeOfExcludingThis(nsWin
     }
 
     *p += nodeSize;
   }
 
   aWindowSizes->mStyleSheets +=
     mStyleSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
                                      aWindowSizes->mMallocSizeOf);
+  aWindowSizes->mStyleSheets +=
+    mCatalogSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
+                                       aWindowSizes->mMallocSizeOf);
+  aWindowSizes->mStyleSheets +=
+    mAdditionalSheets[eAgentSheet].
+      SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
+                          aWindowSizes->mMallocSizeOf);
+  aWindowSizes->mStyleSheets +=
+    mAdditionalSheets[eUserSheet].
+      SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
+                          aWindowSizes->mMallocSizeOf);
+  // Lumping in the loader with the style-sheets size is not ideal,
+  // but most of the things in there are in fact stylesheets, so it
+  // doesn't seem worthwhile to separate it out.
+  aWindowSizes->mStyleSheets +=
+    CSSLoader()->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
+
   aWindowSizes->mDOMOther +=
     mAttrStyleSheet ?
     mAttrStyleSheet->DOMSizeOfIncludingThis(aWindowSizes->mMallocSizeOf) :
     0;
 
   aWindowSizes->mDOMOther +=
     mStyledLinks.SizeOfExcludingThis(NULL, aWindowSizes->mMallocSizeOf);
 
--- a/content/base/src/nsEventSource.cpp
+++ b/content/base/src/nsEventSource.cpp
@@ -76,17 +76,17 @@ nsEventSource::~nsEventSource()
 //-----------------------------------------------------------------------------
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsEventSource)
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsEventSource)
   bool isBlack = tmp->IsBlack();
   if (isBlack || tmp->mWaitingForOnStopRequest) {
     if (tmp->mListenerManager) {
-      tmp->mListenerManager->UnmarkGrayJSListeners();
+      tmp->mListenerManager->MarkForCC();
     }
     if (!isBlack && tmp->PreservingWrapper()) {
       xpc_UnmarkGrayObject(tmp->GetWrapperPreserveColor());
     }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -1321,10 +1321,13 @@ NS_NewChildProcessMessageManager(nsISync
 
 bool
 nsFrameMessageManager::MarkForCC()
 {
   uint32_t len = mListeners.Length();
   for (uint32_t i = 0; i < len; ++i) {
     xpc_TryUnmarkWrappedGrayObject(mListeners[i].mListener);
   }
+  if (mRefCnt.IsPurple()) {
+    mRefCnt.RemovePurple();
+  }
   return true;
 }
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -1465,19 +1465,25 @@ nsObjectLoadingContent::UpdateObjectPara
   }
 
   if (!URIEquals(newURI, mURI)) {
     retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
     LOG(("OBJLC [%p]: Object effective URI changed", this));
     mURI = newURI;
   }
 
-  if (mContentType != newMime) {
+  // We don't update content type when loading, as the type is not final and we
+  // don't want to superfluously change between mOriginalContentType ->
+  // mContentType when doing |obj.data = obj.data| with a channel and differing
+  // type.
+  if (mType != eType_Loading && mContentType != newMime) {
     retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
-    LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)", this, mContentType.get(), newMime.get()));
+    retval = (ParameterUpdateFlags)(retval | eParamContentTypeChanged);
+    LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)",
+         this, mContentType.get(), newMime.get()));
     mContentType = newMime;
   }
 
   return retval;
 }
 
 // Only OnStartRequest should be passing the channel parameter
 nsresult
@@ -1548,16 +1554,22 @@ nsObjectLoadingContent::LoadObject(bool 
       fallbackType = eFallbackDisabled;
     } else if (pluginsupport == NS_ERROR_PLUGIN_BLOCKLISTED) {
       fallbackType = eFallbackBlocklisted;
     } else {
       fallbackType = eFallbackUnsupported;
     }
   }
 
+  // Explicit user activation should reset if the object changes content types
+  if (mActivated && (stateChange & eParamContentTypeChanged)) {
+    LOG(("OBJLC [%p]: Content type changed, clearing activation state", this));
+    mActivated = false;
+  }
+
   // We synchronously start/stop plugin instances below, which may spin the
   // event loop. Re-entering into the load is fine, but at that point the
   // original load call needs to abort when unwinding
   // NOTE this is located *after* the state change check, a subseqent load
   //      with no subsequently changed state will be a no-op.
   if (mIsLoading) {
     LOG(("OBJLC [%p]: Re-entering into LoadObject", this));
   }
@@ -1656,16 +1668,23 @@ nsObjectLoadingContent::LoadObject(bool 
   // reason click-to-play instead
   FallbackType clickToPlayReason;
   if (mType == eType_Plugin && !ShouldPlay(clickToPlayReason)) {
     LOG(("OBJLC [%p]: Marking plugin as click-to-play", this));
     mType = eType_Null;
     fallbackType = clickToPlayReason;
   }
 
+  if (!mActivated && mType != eType_Null) {
+    // Object passed ShouldPlay and !ShouldPreview, so it should be considered
+    // activated until it changes content type
+    LOG(("OBJLC [%p]: Object implicitly activated", this));
+    mActivated = true;
+  }
+
   // Sanity check: We shouldn't have any loaded resources, pending events, or
   // a final listener at this point
   if (mFrameLoader || mPendingInstantiateEvent || mInstanceOwner ||
       mFinalListener)
   {
     NS_NOTREACHED("Trying to load new plugin with existing content");
     rv = NS_ERROR_UNEXPECTED;
     return NS_OK;
@@ -2459,25 +2478,35 @@ nsObjectLoadingContent::NotifyContentObj
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::PlayPlugin()
 {
   if (!nsContentUtils::IsCallerChrome())
     return NS_OK;
 
-  mActivated = true;
-  return LoadObject(true, true);
+  if (!mActivated) {
+    mActivated = true;
+    LOG(("OBJLC [%p]: Activated by user", this));
+  }
+
+  // If we're in a click-to-play or play preview state, we need to reload
+  // Fallback types >= eFallbackClickToPlay are plugin-replacement types, see
+  // header
+  if (mType == eType_Null && mFallbackType >= eFallbackClickToPlay) {
+    return LoadObject(true, true);
+  }
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::GetActivated(bool *aActivated)
 {
-  FallbackType reason;
-  *aActivated = ShouldPlay(reason) && !ShouldPreview();
+  *aActivated = mActivated;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::GetPluginFallbackType(uint32_t* aPluginFallbackType)
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
   *aPluginFallbackType = mFallbackType;
@@ -2485,21 +2514,24 @@ nsObjectLoadingContent::GetPluginFallbac
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::CancelPlayPreview()
 {
   if (!nsContentUtils::IsCallerChrome())
     return NS_ERROR_NOT_AVAILABLE;
 
-  if (mPlayPreviewCanceled || mActivated)
-    return NS_OK;
+  mPlayPreviewCanceled = true;
+  
+  // If we're in play preview state already, reload
+  if (mType == eType_Null && mFallbackType == eFallbackPlayPreview) {
+    return LoadObject(true, true);
+  }
 
-  mPlayPreviewCanceled = true;
-  return LoadObject(true, true);
+  return NS_OK;
 }
 
 bool
 nsObjectLoadingContent::ShouldPreview()
 {
   if (mPlayPreviewCanceled || mActivated)
     return false;
 
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -72,16 +72,18 @@ class nsObjectLoadingContent : public ns
       // The plugin is considered outdated, but not disabled
       eFallbackOutdated = nsIObjectLoadingContent::PLUGIN_OUTDATED,
       // The plugin has crashed
       eFallbackCrashed = nsIObjectLoadingContent::PLUGIN_CRASHED,
       // Suppressed by security policy
       eFallbackSuppressed = nsIObjectLoadingContent::PLUGIN_SUPPRESSED,
       // Blocked by content policy
       eFallbackUserDisabled = nsIObjectLoadingContent::PLUGIN_USER_DISABLED,
+      /// ** All values >= eFallbackClickToPlay are plugin placeholder types
+      ///    that would be replaced by a real plugin if activated (PlayPlugin())
       // The plugin is disabled until the user clicks on it
       eFallbackClickToPlay = nsIObjectLoadingContent::PLUGIN_CLICK_TO_PLAY,
       // The plugin is vulnerable (update available)
       eFallbackVulnerableUpdatable = nsIObjectLoadingContent::PLUGIN_VULNERABLE_UPDATABLE,
       // The plugin is vulnerable (no update available)
       eFallbackVulnerableNoUpdate = nsIObjectLoadingContent::PLUGIN_VULNERABLE_NO_UPDATE,
       // The plugin is disabled and play preview content is displayed until
       // the extension code enables it by sending the MozPlayPlugin event
@@ -217,17 +219,24 @@ class nsObjectLoadingContent : public ns
     // Object parameter changes returned by UpdateObjectParameters
     enum ParameterUpdateFlags {
       eParamNoChange           = 0,
       // Parameters that potentially affect the channel changed
       // - mOriginalURI, mOriginalContentType
       eParamChannelChanged     = PR_BIT(0),
       // Parameters that affect displayed content changed
       // - mURI, mContentType, mType, mBaseURI
-      eParamStateChanged       = PR_BIT(1)
+      eParamStateChanged       = PR_BIT(1),
+      // The effective content type changed, independant of object type. This
+      // can happen when changing from Loading -> Final type, but doesn't
+      // necessarily happen when changing between object types. E.g., if a PDF
+      // handler was installed between the last load of this object and now, we
+      // might change from eType_Document -> eType_Plugin without changing
+      // ContentType
+      eParamContentTypeChanged = PR_BIT(2)
     };
 
     /**
      * Loads fallback content with the specified FallbackType
      *
      * @param aType   FallbackType value for type of fallback we're loading
      * @param aNotify Send notifications and events. If false, caller is
      *                responsible for doing so
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -578,17 +578,17 @@ nsXMLHttpRequest::SetRequestObserver(nsI
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest)
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest)
   bool isBlack = tmp->IsBlack();
   if (isBlack || tmp->mWaitingForOnStopRequest) {
     if (tmp->mListenerManager) {
-      tmp->mListenerManager->UnmarkGrayJSListeners();
+      tmp->mListenerManager->MarkForCC();
     }
     if (!isBlack && tmp->PreservingWrapper()) {
       xpc_UnmarkGrayObject(tmp->GetWrapperPreserveColor());
     }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -76,18 +76,17 @@ public:
                             const PRUnichar *aEncoderOptions,
                             nsIInputStream **aStream) = 0;
 
   // If this canvas context can be represented with a simple Thebes surface,
   // return the surface.  Otherwise returns an error.
   NS_IMETHOD GetThebesSurface(gfxASurface **surface) = 0;
   
   // This gets an Azure SourceSurface for the canvas, this will be a snapshot
-  // of the canvas at the time it was called. This will return null for a
-  // non-azure canvas.
+  // of the canvas at the time it was called.
   virtual mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot() = 0;
 
   // If this context is opaque, the backing store of the canvas should
   // be created as opaque; all compositing operators should assume the
   // dst alpha is always 1.0.  If this is never called, the context
   // defaults to false (not opaque).
   NS_IMETHOD SetIsOpaque(bool isOpaque) = 0;
 
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -2599,18 +2599,16 @@ CanvasRenderingContext2D::SetFont(const 
 
   nsIPresShell* presShell = GetPresShell();
   if (!presShell) {
     error.Throw(NS_ERROR_FAILURE);
     return;
   }
   nsIDocument* document = presShell->GetDocument();
 
-  nsCOMArray<nsIStyleRule> rules;
-
   nsRefPtr<css::StyleRule> rule;
   error = CreateFontStyleRule(font, document, getter_AddRefs(rule));
 
   if (error.Failed()) {
     return;
   }
 
   css::Declaration *declaration = rule->GetDeclaration();
@@ -2624,17 +2622,18 @@ CanvasRenderingContext2D::SetFont(const 
     declaration->GetNormalBlock()->ValueFor(eCSSProperty_font_size_adjust);
   if (!fsaVal || (fsaVal->GetUnit() != eCSSUnit_None &&
                   fsaVal->GetUnit() != eCSSUnit_System_Font)) {
       // We got an all-property value or a syntax error.  The spec says
       // this value must be ignored.
     return;
   }
 
-  rules.AppendObject(rule);
+  nsTArray< nsCOMPtr<nsIStyleRule> > rules;
+  rules.AppendElement(rule);
 
   nsStyleSet* styleSet = presShell->StyleSet();
 
   // have to get a parent style context for inherit-like relative
   // values (2em, bolder, etc.)
   nsRefPtr<nsStyleContext> parentContext;
 
   if (mCanvasElement && mCanvasElement->IsInDoc()) {
@@ -2649,18 +2648,18 @@ CanvasRenderingContext2D::SetFont(const 
     error = CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
                                 document,
                                 getter_AddRefs(parentRule));
 
     if (error.Failed()) {
       return;
     }
 
-    nsCOMArray<nsIStyleRule> parentRules;
-    parentRules.AppendObject(parentRule);
+    nsTArray< nsCOMPtr<nsIStyleRule> > parentRules;
+    parentRules.AppendElement(parentRule);
     parentContext = styleSet->ResolveStyleForRules(nullptr, parentRules);
   }
 
   if (!parentContext) {
     error.Throw(NS_ERROR_FAILURE);
     return;
   }
 
--- a/content/canvas/src/Makefile.in
+++ b/content/canvas/src/Makefile.in
@@ -48,16 +48,17 @@ CPPSRCS += \
 	WebGLContextUtils.cpp \
 	WebGLContextReporter.cpp \
 	WebGLContextValidate.cpp \
 	WebGLElementArrayCache.cpp \
 	WebGLExtensionBase.cpp \
 	WebGLExtensionCompressedTextureATC.cpp \
 	WebGLExtensionCompressedTexturePVRTC.cpp \
 	WebGLExtensionCompressedTextureS3TC.cpp \
+	WebGLExtensionDebugRendererInfo.cpp \
 	WebGLExtensionDepthTexture.cpp \
 	WebGLExtensionLoseContext.cpp \
 	WebGLExtensionStandardDerivatives.cpp \
 	WebGLExtensionTextureFilterAnisotropic.cpp \
 	WebGLExtensionTextureFloat.cpp \
 	WebGLFramebuffer.cpp \
 	WebGLProgram.cpp \
 	WebGLRenderbuffer.cpp \
@@ -66,16 +67,20 @@ CPPSRCS += \
 	WebGLTexelConversions.cpp \
 	WebGLTexture.cpp \
 	WebGLUniformLocation.cpp \
 	$(NULL)
 
 DEFINES += -DUSE_ANGLE
 USE_ANGLE=1
 
+LOCAL_INCLUDES += \
+	-I$(topsrcdir)/js/xpconnect/wrappers \
+	$(NULL)
+
 else
 
 CPPSRCS += WebGLContextNotSupported.cpp
 
 endif
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLContext.h"
 #include "WebGLExtensions.h"
 #include "WebGLContextUtils.h"
 
+#include "AccessCheck.h"
 #include "nsIConsoleService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIClassInfoImpl.h"
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
 #include "nsError.h"
 #include "nsIGfxInfo.h"
 
@@ -943,17 +944,17 @@ WebGLContext::MozGetUnderlyingParamStrin
 
     default:
         return NS_ERROR_INVALID_ARG;
     }
 
     return NS_OK;
 }
 
-bool WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
+bool WebGLContext::IsExtensionSupported(JSContext *cx, WebGLExtensionID ext) const
 {
     if (mDisableExtensions) {
         return false;
     }
 
     switch (ext) {
         case OES_standard_derivatives:
         case WEBGL_lose_context:
@@ -993,16 +994,18 @@ bool WebGLContext::IsExtensionSupported(
                      gl->IsExtensionSupported(GLContext::EXT_packed_depth_stencil))
             {
                 return true;
             }
             else
             {
                 return false;
             }
+        case WEBGL_debug_renderer_info:
+            return xpc::AccessCheck::isChrome(js::GetContextCompartment(cx));
         default:
             MOZ_ASSERT(false, "should not get there.");
     }
 
     MOZ_ASSERT(false, "should not get there.");
     return false;
 }
 
@@ -1046,27 +1049,31 @@ WebGLContext::GetExtension(JSContext *cx
     else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_atc"))
     {
         ext = WEBGL_compressed_texture_atc;
     }
     else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_pvrtc"))
     {
         ext = WEBGL_compressed_texture_pvrtc;
     }
+    else if (CompareWebGLExtensionName(name, "WEBGL_debug_renderer_info"))
+    {
+        ext = WEBGL_debug_renderer_info;
+    }
     else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_depth_texture"))
     {
         ext = WEBGL_depth_texture;
     }
 
     if (ext == WebGLExtensionID_unknown_extension) {
       return nullptr;
     }
 
     // step 2: check if the extension is supported
-    if (!IsExtensionSupported(ext)) {
+    if (!IsExtensionSupported(cx, ext)) {
         return nullptr;
     }
 
     // step 3: if the extension hadn't been previously been created, create it now, thus enabling it
     if (!IsExtensionEnabled(ext)) {
         WebGLExtensionBase *obj = nullptr;
         switch (ext) {
             case OES_standard_derivatives:
@@ -1082,16 +1089,19 @@ WebGLContext::GetExtension(JSContext *cx
                 obj = new WebGLExtensionCompressedTextureS3TC(this);
                 break;
             case WEBGL_compressed_texture_atc:
                 obj = new WebGLExtensionCompressedTextureATC(this);
                 break;
             case WEBGL_compressed_texture_pvrtc:
                 obj = new WebGLExtensionCompressedTexturePVRTC(this);
                 break;
+            case WEBGL_debug_renderer_info:
+                obj = new WebGLExtensionDebugRendererInfo(this);
+                break;
             case WEBGL_depth_texture:
                 obj = new WebGLExtensionDepthTexture(this);
                 break;
             case OES_texture_float:
                 obj = new WebGLExtensionTextureFloat(this);
                 break;
             default:
                 MOZ_ASSERT(false, "should not get there.");
@@ -1347,39 +1357,41 @@ WebGLContext::ForceLoseContext()
 
 void
 WebGLContext::ForceRestoreContext()
 {
     mContextStatus = ContextLostAwaitingRestore;
 }
 
 void
-WebGLContext::GetSupportedExtensions(Nullable< nsTArray<nsString> > &retval)
+WebGLContext::GetSupportedExtensions(JSContext *cx, Nullable< nsTArray<nsString> > &retval)
 {
     retval.SetNull();
     if (!IsContextStable())
         return;
 
     nsTArray<nsString>& arr = retval.SetValue();
 
-    if (IsExtensionSupported(OES_texture_float))
+    if (IsExtensionSupported(cx, OES_texture_float))
         arr.AppendElement(NS_LITERAL_STRING("OES_texture_float"));
-    if (IsExtensionSupported(OES_standard_derivatives))
+    if (IsExtensionSupported(cx, OES_standard_derivatives))
         arr.AppendElement(NS_LITERAL_STRING("OES_standard_derivatives"));
-    if (IsExtensionSupported(EXT_texture_filter_anisotropic))
+    if (IsExtensionSupported(cx, EXT_texture_filter_anisotropic))
         arr.AppendElement(NS_LITERAL_STRING("EXT_texture_filter_anisotropic"));
-    if (IsExtensionSupported(WEBGL_lose_context))
+    if (IsExtensionSupported(cx, WEBGL_lose_context))
         arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_lose_context"));
-    if (IsExtensionSupported(WEBGL_compressed_texture_s3tc))
+    if (IsExtensionSupported(cx, WEBGL_compressed_texture_s3tc))
         arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_s3tc"));
-    if (IsExtensionSupported(WEBGL_compressed_texture_atc))
+    if (IsExtensionSupported(cx, WEBGL_compressed_texture_atc))
         arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_atc"));
-    if (IsExtensionSupported(WEBGL_compressed_texture_pvrtc))
+    if (IsExtensionSupported(cx, WEBGL_compressed_texture_pvrtc))
         arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_pvrtc"));
-    if (IsExtensionSupported(WEBGL_depth_texture))
+    if (IsExtensionSupported(cx, WEBGL_debug_renderer_info))
+        arr.AppendElement(NS_LITERAL_STRING("WEBGL_debug_renderer_info"));
+    if (IsExtensionSupported(cx, WEBGL_depth_texture))
         arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_depth_texture"));
 }
 
 //
 // XPCOM goop
 //
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -482,17 +482,19 @@ class WebGLContext :
     friend class WebGLExtensionCompressedTexturePVRTC;
     friend class WebGLExtensionDepthTexture;
 
     enum {
         UNPACK_FLIP_Y_WEBGL = 0x9240,
         UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
         CONTEXT_LOST_WEBGL = 0x9242,
         UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243,
-        BROWSER_DEFAULT_WEBGL = 0x9244
+        BROWSER_DEFAULT_WEBGL = 0x9244,
+        UNMASKED_VENDOR_WEBGL = 0x9245,
+        UNMASKED_RENDERER_WEBGL = 0x9246
     };
 
 public:
     WebGLContext();
     virtual ~WebGLContext();
 
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
@@ -637,18 +639,18 @@ public:
     WebGLsizei DrawingBufferHeight() const {
         if (!IsContextStable())
             return 0;
         return mHeight;
     }
         
     JSObject *GetContextAttributes(ErrorResult &rv);
     bool IsContextLost() const { return !IsContextStable(); }
-    void GetSupportedExtensions(dom::Nullable< nsTArray<nsString> > &retval);
-    JSObject* GetExtension(JSContext* ctx, const nsAString& aName, ErrorResult& rv);
+    void GetSupportedExtensions(JSContext *cx, dom::Nullable< nsTArray<nsString> > &retval);
+    JSObject* GetExtension(JSContext* cx, const nsAString& aName, ErrorResult& rv);
     void ActiveTexture(WebGLenum texture);
     void AttachShader(WebGLProgram* program, WebGLShader* shader);
     void BindAttribLocation(WebGLProgram* program, WebGLuint location,
                             const nsAString& name);
     void BindBuffer(WebGLenum target, WebGLBuffer* buf);
     void BindFramebuffer(WebGLenum target, WebGLFramebuffer* wfb);
     void BindRenderbuffer(WebGLenum target, WebGLRenderbuffer* wrb);
     void BindTexture(WebGLenum target, WebGLTexture *tex);
@@ -1182,33 +1184,34 @@ protected:
         // The context is lost, an event has been sent to the script, and the
         // script correctly handled the event. We are waiting for the context to
         // be restored.
         ContextLostAwaitingRestore
     };
 
     // extensions
     enum WebGLExtensionID {
-        OES_texture_float,
+        EXT_texture_filter_anisotropic,
         OES_standard_derivatives,
-        EXT_texture_filter_anisotropic,
-        WEBGL_lose_context,
-        WEBGL_compressed_texture_s3tc,
+        OES_texture_float,
         WEBGL_compressed_texture_atc,
         WEBGL_compressed_texture_pvrtc,
+        WEBGL_compressed_texture_s3tc,
+        WEBGL_debug_renderer_info,
         WEBGL_depth_texture,
+        WEBGL_lose_context,
         WebGLExtensionID_unknown_extension
     };
     nsTArray<nsRefPtr<WebGLExtensionBase> > mExtensions;
 
     // returns true if the extension has been enabled by calling getExtension.
     bool IsExtensionEnabled(WebGLExtensionID ext) const;
 
-    // returns true if the extension is supported (as returned by getSupportedExtensions)
-    bool IsExtensionSupported(WebGLExtensionID ext) const;
+    // returns true if the extension is supported for this JSContext (this decides what getSupportedExtensions exposes)
+    bool IsExtensionSupported(JSContext *cx, WebGLExtensionID ext) const;
 
     nsTArray<WebGLenum> mCompressedTextureFormats;
 
     bool InitAndValidateGL();
     bool ValidateBuffers(int32_t *maxAllowedCount, const char *info);
     bool ValidateCapabilityEnum(WebGLenum cap, const char *info);
     bool ValidateBlendEquationEnum(WebGLenum cap, const char *info);
     bool ValidateBlendFuncDstEnum(WebGLenum mode, const char *info);
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -1897,16 +1897,36 @@ WebGLContext::GetParameter(JSContext* cx
             return StringValue(cx, "Mozilla", rv);
         case LOCAL_GL_RENDERER:
             return StringValue(cx, "Mozilla", rv);
         case LOCAL_GL_VERSION:
             return StringValue(cx, "WebGL 1.0", rv);
         case LOCAL_GL_SHADING_LANGUAGE_VERSION:
             return StringValue(cx, "WebGL GLSL ES 1.0", rv);
 
+        // Privileged string params exposed by WEBGL_debug_renderer_info:
+        case UNMASKED_VENDOR_WEBGL:
+        case UNMASKED_RENDERER_WEBGL:
+        {
+            // The privilege check is done in WebGLContext::IsExtensionSupported.
+            // So here we just have to check that the extension is enabled.
+            if (!IsExtensionEnabled(WEBGL_debug_renderer_info)) {
+                ErrorInvalidEnumInfo("getParameter: parameter", pname);
+                return JS::NullValue();
+            }
+            GLenum glstringname = LOCAL_GL_NONE;
+            if (pname == UNMASKED_VENDOR_WEBGL) {
+                glstringname = LOCAL_GL_VENDOR;
+            } else if (pname == UNMASKED_RENDERER_WEBGL) {
+                glstringname = LOCAL_GL_RENDERER;
+            }
+            const char* string = reinterpret_cast<const char*>(gl->fGetString(glstringname));
+            return StringValue(cx, string, rv);
+        }
+
         //
         // Single-value params
         //
 
         // unsigned int
         case LOCAL_GL_CULL_FACE_MODE:
         case LOCAL_GL_FRONT_FACE:
         case LOCAL_GL_ACTIVE_TEXTURE:
@@ -2006,17 +2026,17 @@ WebGLContext::GetParameter(JSContext* cx
 
 // float
         case LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT:
             if (IsExtensionEnabled(EXT_texture_filter_anisotropic)) {
                 GLfloat f = 0.f;
                 gl->fGetFloatv(pname, &f);
                 return JS::DoubleValue(f);
             } else {
-                ErrorInvalidEnum("getParameter: parameter", pname);
+                ErrorInvalidEnumInfo("getParameter: parameter", pname);
                 return JS::NullValue();
             }
         case LOCAL_GL_DEPTH_CLEAR_VALUE:
         case LOCAL_GL_LINE_WIDTH:
         case LOCAL_GL_POLYGON_OFFSET_FACTOR:
         case LOCAL_GL_POLYGON_OFFSET_UNITS:
         case LOCAL_GL_SAMPLE_COVERAGE_VALUE:
         {
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLExtensionDebugRendererInfo.cpp
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLContext.h"
+#include "WebGLExtensions.h"
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
+
+using namespace mozilla;
+
+WebGLExtensionDebugRendererInfo::WebGLExtensionDebugRendererInfo(WebGLContext* context)
+    : WebGLExtensionBase(context)
+{
+}
+
+WebGLExtensionDebugRendererInfo::~WebGLExtensionDebugRendererInfo()
+{
+}
+
+IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDebugRendererInfo)
--- a/content/canvas/src/WebGLExtensions.h
+++ b/content/canvas/src/WebGLExtensions.h
@@ -61,16 +61,26 @@ class WebGLExtensionCompressedTextureS3T
 {
 public:
     WebGLExtensionCompressedTextureS3TC(WebGLContext*);
     virtual ~WebGLExtensionCompressedTextureS3TC();
 
     DECL_WEBGL_EXTENSION_GOOP
 };
 
+class WebGLExtensionDebugRendererInfo
+    : public WebGLExtensionBase
+{
+public:
+    WebGLExtensionDebugRendererInfo(WebGLContext*);
+    virtual ~WebGLExtensionDebugRendererInfo();
+
+    DECL_WEBGL_EXTENSION_GOOP
+};
+
 class WebGLExtensionDepthTexture
     : public WebGLExtensionBase
 {
 public:
     WebGLExtensionDepthTexture(WebGLContext*);
     virtual ~WebGLExtensionDepthTexture();
 
     DECL_WEBGL_EXTENSION_GOOP
--- a/content/canvas/test/Makefile.in
+++ b/content/canvas/test/Makefile.in
@@ -3,17 +3,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = @relativesrcdir@
-DIRS		+= webgl crossorigin
+DIRS		+= webgl crossorigin chrome
 
 # TEST_DIRS += compiled
 
 include $(DEPTH)/config/autoconf.mk
 MOCHITEST_FILES = \
 	test_canvas.html \
 	image_transparent50.png \
 	image_redtransparent.png \
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/chrome/Makefile.in
@@ -0,0 +1,19 @@
+#
+# 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/.
+
+DEPTH           = @DEPTH@
+topsrcdir       = @top_srcdir@
+srcdir          = @srcdir@
+VPATH           = @srcdir@
+relativesrcdir  = @relativesrcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MOCHITEST_CHROME_FILES = \
+	test_webgl_debug_renderer_info.html \
+	nonchrome_webgl_debug_renderer_info.html \
+	$(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<script>
+
+// This file has the portion of the test_webgl_renderer_info chrome mochitest
+// that has to run as non-chrome to check that this WebGL extension is not exposed to content
+
+// we can't call the chrome Mochitest ok() function ourselves from non-chrome code.
+// So we remote it to the chrome test.
+
+function ok(res, msg) {
+  // Note we post to ourselves as posting to the chrome code doesn't seem to work here.
+  // This works by having the chrome code put an event handler on our own window.
+  window.postMessage({ subTestFinished: true, result: res, message: msg }, "*");
+}
+
+function messageListener(e) {
+  // This is how the chrome test tells us to start running -- we have to wait for this
+  // message to avoid running before it's set up its event handler.
+  if (e.data == "run") {
+    run();
+  }
+}
+
+window.addEventListener("message", messageListener, true);
+
+function run() {
+  const UNMASKED_VENDOR_WEBGL = 0x9245;
+  const UNMASKED_RENDERER_WEBGL = 0x9246;
+
+  var canvas = document.createElement("canvas");
+  var gl = canvas.getContext("experimental-webgl");
+
+  ok(!gl.getError(), "getError on newly created WebGL context should return NO_ERROR");
+
+  ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+      "Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
+  ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+      "Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
+
+  var exts = gl.getSupportedExtensions();
+  ok(exts.indexOf("WEBGL_debug_renderer_info") == -1,
+      "WEBGL_debug_renderer_info should not be listed by getSupportedExtensions in non-chrome contexts");
+  var debugRendererInfoExtension = gl.getExtension("WEBGL_debug_renderer_info");
+  ok(!debugRendererInfoExtension,
+      "WEBGL_debug_renderer_info should not be available through getExtension in non-chrome contexts");
+
+  ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+      "Should not be able to query UNMASKED_VENDOR_WEBGL if enabling WEBGL_debug_renderer_info failed");
+  ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+      "Should not be able to query UNMASKED_RENDERER_WEBGL if enabling WEBGL_debug_renderer_info failed");
+
+  window.postMessage({allTestsFinished: true}, "*");
+}
+
+</script>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/chrome/test_webgl_debug_renderer_info.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+  <title>Test for WEBGL_debug_renderer_info chrome-only extension</title>
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<pre id="test">
+<script>
+
+const UNMASKED_VENDOR_WEBGL = 0x9245;
+const UNMASKED_RENDERER_WEBGL = 0x9246;
+
+function isNonEmptyString(s)
+{
+  return s && (typeof s) == "string";
+}
+
+function messageListener(e) {
+  if (e.data.allTestsFinished) {
+    SimpleTest.finish();
+  } else if (e.data.subTestFinished) {
+    ok(e.data.result, "content iframe: " + e.data.message);
+  }
+}
+
+function checkChromeCase(canvas) {
+
+  var gl = canvas.getContext("experimental-webgl");
+  ok(!gl.getError(), "getError on newly created WebGL context should return NO_ERROR");
+
+  ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+     "Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
+  ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+     "Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
+
+  var exts = gl.getSupportedExtensions();
+  ok(exts.indexOf("WEBGL_debug_renderer_info") != -1,
+     "WEBGL_debug_renderer_info should be listed by getSupportedExtensions in chrome contexts");
+  var debugRendererInfoExtension = gl.getExtension("WEBGL_debug_renderer_info");
+  ok(debugRendererInfoExtension,
+     "WEBGL_debug_renderer_info should be available through getExtension in chrome contexts");
+
+  ok(debugRendererInfoExtension.UNMASKED_VENDOR_WEBGL == UNMASKED_VENDOR_WEBGL,
+     "UNMASKED_VENDOR_WEBGL has the correct value");
+  ok(debugRendererInfoExtension.UNMASKED_RENDERER_WEBGL == UNMASKED_RENDERER_WEBGL,
+     "UNMASKED_RENDERER_WEBGL has the correct value");
+
+  ok(isNonEmptyString(gl.getParameter(UNMASKED_VENDOR_WEBGL)) && gl.getError() == gl.NO_ERROR,
+     "Should be able to query UNMASKED_VENDOR_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
+  ok(isNonEmptyString(gl.getParameter(UNMASKED_RENDERER_WEBGL)) && gl.getError() == gl.NO_ERROR,
+     "Should be able to query UNMASKED_RENDERER_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
+}
+
+function main()
+{
+  SimpleTest.waitForExplicitFinish();
+
+  checkChromeCase(document.createElement("canvas"));
+
+  // Now run the non-chrome code to verify the security of this WebGL chrome-only extension.
+
+  var iframe = document.createElement("iframe");
+  iframe.src = "http://mochi.test:8888/chrome/content/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html";
+
+  iframe.onload = function () {
+
+    // test that chrome can get WEBGL_debug_renderer_info on a canvas on the iframe...
+    // this is useful to check in itself, and is also useful so the subsequent non-chrome test
+    // will also test that doing so doesn't confuse our chrome-only check.
+    checkChromeCase(iframe.contentDocument.createElement("canvas"));
+
+    iframe.contentWindow.addEventListener("message", messageListener, false);
+    iframe.contentWindow.postMessage("run", "*");
+  };
+
+  document.body.appendChild(iframe);
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
--- a/content/canvas/test/test_canvas.html
+++ b/content/canvas/test/test_canvas.html
@@ -35,27 +35,16 @@ function IsMacOSX10_5orOlder() {
         // Mac OS 10.5 would be Darwin version 9. the |version| string we've got here
         // is the Darwin version.
         is105orOlder = (parseFloat(version) < 10.0);
     }
     return is105orOlder;
 }
 
 
-function IsAzureEnabled() {
-  var enabled = false;
-
-  try {
-    var backend = Cc["@mozilla.org/gfx/info;1"].getService(SpecialPowers.Ci.nsIGfxInfo).getInfo().AzureCanvasBackend;
-    enabled = (backend != "none");
-  } catch (e) { }
-
-  return enabled;
-}
-
 function IsAzureSkia() {
   var enabled = false;
   
   try {
     var backend = Cc["@mozilla.org/gfx/info;1"].getService(SpecialPowers.Ci.nsIGfxInfo).getInfo().AzureCanvasBackend;
     enabled = (backend == "skia");
   } catch (e) { }
 
@@ -3533,21 +3522,17 @@ function test_2d_drawImage_null() {
 
 var canvas = document.getElementById('c121');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.drawImage(null, 0, 0);
 } catch (e) { _thrown = e };
 
-if (IsAzureEnabled()) {
-  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-} else {
-  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-}
+ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
 
 }
 </script>
 
 <!-- [[[ test_2d.drawImage.outsidesource.html ]]] -->
 
 <p>Canvas test: 2d.drawImage.outsidesource</p>
 <canvas id="c122" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
@@ -3714,48 +3699,32 @@ isPixel(ctx, 50,25, 0,255,0,255, 2);
 function test_2d_drawImage_wrongtype() {
 
 var canvas = document.getElementById('c127');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.drawImage(undefined, 0, 0);
 } catch (e) { _thrown = e }; 
-if (IsAzureEnabled()) {
-  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-} else {
-  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-}
+ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
 
 var _thrown = undefined; try {
   ctx.drawImage(0, 0, 0);
 } catch (e) { _thrown = e };
-if (IsAzureEnabled()) {
-  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-} else {
-  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-}
+ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
 
 var _thrown = undefined; try {
   ctx.drawImage("", 0, 0);
 } catch (e) { _thrown = e };
-if (IsAzureEnabled()) {
-  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-} else {
-  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-}
+ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
 
 var _thrown = undefined; try {
   ctx.drawImage(document.createElement('p'), 0, 0);
 } catch (e) { _thrown = e };
-if (IsAzureEnabled()) {
-  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-} else {
-  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-}
+ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
 
 }
 </script>
 
 <!-- [[[ test_2d.drawImage.zerosource.html ]]] -->
 
 <p>Canvas test: 2d.drawImage.zerosource</p>
 <!-- Testing: drawImage with zero-sized source rectangle throws INDEX_SIZE_ERR -->
@@ -6146,26 +6115,19 @@ g.addColorStop(0, '#f00');
 g.addColorStop(0.25, '#0f0');
 g.addColorStop(0.75, '#0f0');
 g.addColorStop(1, '#f00');
 ctx.fillStyle = g;
 ctx.fillRect(0, 0, 100, 50);
 ctx.translate(-50, 0);
 ctx.fillRect(50, 0, 100, 50);
 
-if (IsAzureEnabled()) {
-  isPixel(ctx, 25,25, 0,255,0,255, 0);
-} else {
-  todo_isPixel(ctx, 25,25, 0,255,0,255, 0);
-}
-
+isPixel(ctx, 25,25, 0,255,0,255, 0);
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 isPixel(ctx, 75,25, 0,255,0,255, 0);
-
-
 }
 </script>
 
 <!-- [[[ test_2d.gradient.object.compare.html ]]] -->
 
 <p>Canvas test: 2d.gradient.object.compare</p>
 <canvas id="c224" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
@@ -7343,25 +7305,19 @@ g.addColorStop(0.5, '#0f0');
 g.addColorStop(0.51, '#f00');
 g.addColorStop(1, '#f00');
 ctx.fillStyle = g;
 ctx.fillRect(0, 0, 100, 50);
 ctx.translate(50, 25);
 ctx.scale(10, 10);
 ctx.fillRect(-5, -2.5, 10, 5);
 
-if (IsAzureEnabled()) {
-  isPixel(ctx, 25,25, 0,255,0,255, 0);
-  isPixel(ctx, 50,25, 0,255,0,255, 0);
-  isPixel(ctx, 75,25, 0,255,0,255, 0);
-} else {
-  todo_isPixel(ctx, 25,25, 0,255,0,255, 0);
-  todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
-  todo_isPixel(ctx, 75,25, 0,255,0,255, 0);
-}
+isPixel(ctx, 25,25, 0,255,0,255, 0);
+isPixel(ctx, 50,25, 0,255,0,255, 0);
+isPixel(ctx, 75,25, 0,255,0,255, 0);
 
 }
 </script>
 
 <!-- [[[ test_2d.imageData.create.basic.html ]]] -->
 
 <p>Canvas test: 2d.imageData.create.basic - bug 433004</p>
 <!-- Testing: createImageData() exists and returns something -->
@@ -7712,21 +7668,17 @@ var _thrown = undefined; try {
 function test_2d_imageData_create1_zero() {
 
 var canvas = document.getElementById('c262a');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.createImageData(null);
 } catch (e) { _thrown = e };
-if (IsAzureEnabled()) {
-  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-} else {
-  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-}
+ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
 
 
 }
 </script>
 
 <!-- [[[ test_2d.imageData.get.basic.html ]]] -->
 
 <p>Canvas test: 2d.imageData.get.basic</p>
@@ -9228,21 +9180,17 @@ var _thrown = undefined; try {
 function test_2d_imageData_put_null() {
 
 var canvas = document.getElementById('c300');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.putImageData(null, 0, 0);
 } catch (e) { _thrown = e };
-if (IsAzureEnabled()) {
-  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-} else {
-  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-}
+ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
 
 }
 </script>
 
 <!-- [[[ test_2d.imageData.put.path.html ]]] -->
 
 <p>Canvas test: 2d.imageData.put.path</p>
 <!-- Testing: putImageData() does not affect the current path -->
@@ -9344,42 +9292,27 @@ function test_2d_imageData_put_wrongtype
 
 var canvas = document.getElementById('c304');
 var ctx = canvas.getContext('2d');
 
 var imgdata = { width: 1, height: 1, data: [255, 0, 0, 255] };
 var _thrown = undefined; try {
   ctx.putImageData(imgdata, 0, 0);
 } catch (e) { _thrown = e }; 
-if (IsAzureEnabled()) {
-  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-} else {
-  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-}
+ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
 
 var _thrown = undefined; try {
   ctx.putImageData("cheese", 0, 0);
 } catch (e) { _thrown = e };
-if (IsAzureEnabled()) {
-  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-} else {
-  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-}
+ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
 
 var _thrown = undefined; try {
   ctx.putImageData(42, 0, 0);
 } catch (e) { _thrown = e };
-if (IsAzureEnabled()) {
-  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-} else {
-  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-}
-
-
-
+ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
 }
 </script>
 
 <!-- [[[ test_2d.line.cap.butt.html ]]] -->
 
 <p>Canvas test: 2d.line.cap.butt</p>
 <!-- Testing: lineCap 'butt' is rendered correctly -->
 <canvas id="c305" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
@@ -11676,23 +11609,17 @@ ctx.fillStyle = '#f00'
 ctx.fillRect(0, 0, 100, 50);
 ctx.lineWidth = 50;
 ctx.strokeStyle = '#0f0';
 ctx.beginPath();
 ctx.moveTo(0, 25);
 ctx.arc(200, 25, 0, 0, Math.PI, true);
 ctx.stroke();
 
-if (IsAzureEnabled()) {
-  isPixel(ctx, 50,25, 0,255,0,255, 0);
-} else {
-  todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
-}
-
-
+isPixel(ctx, 50,25, 0,255,0,255, 0);
 }
 </script>
 
 <!-- [[[ test_2d.path.arcTo.coincide.1.html ]]] -->
 
 <p>Canvas test: 2d.path.arcTo.coincide.1</p>
 <!-- Testing: arcTo() has no effect if P0 = P1 -->
 <canvas id="c359" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
@@ -14420,17 +14347,17 @@ ctx.lineCap = 'round';
 ctx.lineJoin = 'round';
 
 ctx.beginPath();
 ctx.moveTo(50, 25);
 ctx.lineTo(50, 25);
 ctx.closePath();
 ctx.stroke();
 
-if (IsAzureEnabled() && IsAzureSkia()) {
+if (IsAzureSkia()) {
   isPixel(ctx, 50,25, 0,255,0,255, 0);
 } else {
   todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
 }
 
 }
 </script>
 
@@ -14496,17 +14423,17 @@ ctx.moveTo(50, 25);
 ctx.quadraticCurveTo(50, 25, 50, 25);
 ctx.stroke();
 
 ctx.beginPath();
 ctx.moveTo(50, 25);
 ctx.bezierCurveTo(50, 25, 50, 25, 50, 25);
 ctx.stroke();
 
-if (IsAzureEnabled() && IsAzureSkia()) {
+if (IsAzureSkia()) {
   isPixel(ctx, 50,25, 0,255,0,255, 0);
 } else {
   todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
 }
 
 
 }
 </script>
@@ -14533,17 +14460,17 @@ ctx.lineWidth = 100;
 ctx.lineCap = 'round';
 ctx.lineJoin = 'round';
 
 ctx.beginPath();
 ctx.moveTo(50, 25);
 ctx.lineTo(50, 25);
 ctx.stroke();
 
-if (IsAzureEnabled() && IsAzureSkia()) {
+if (IsAzureSkia()) {
   isPixel(ctx, 50,25, 0,255,0,255, 0);
 } else {
   todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
 }
 
 }
 </script>
 
@@ -14570,17 +14497,17 @@ ctx.lineCap = 'round';
 ctx.lineJoin = 'round';
 
 ctx.beginPath();
 ctx.rect(50, 25, 0, 0);
 ctx.stroke();
 
 ctx.strokeRect(50, 25, 0, 0);
 
-if (IsAzureEnabled() && IsAzureSkia()) {
+if (IsAzureSkia()) {
   isPixel(ctx, 50,25, 0,255,0,255, 0);
 } else {
   todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
 }
 
 }
 </script>
 
@@ -15168,23 +15095,17 @@ var _thrown = undefined; try {
 function test_2d_pattern_image_null() {
 
 var canvas = document.getElementById('c467');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.createPattern(null, 'repeat');
 } catch (e) { _thrown = e };
-if (IsAzureEnabled()) {
-  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-} else {
-  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-}
-
-
+ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
 }
 </script>
 
 <!-- [[[ test_2d.pattern.image.string.html ]]] -->
 
 <p>Canvas test: 2d.pattern.image.string</p>
 <canvas id="c468" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
@@ -15192,23 +15113,17 @@ if (IsAzureEnabled()) {
 function test_2d_pattern_image_string() {
 
 var canvas = document.getElementById('c468');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.createPattern('image_red.png', 'repeat');
 } catch (e) { _thrown = e };
-if (IsAzureEnabled()) {
-  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-} else {
-  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-}
-
-
+ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
 }
 </script>
 
 <!-- [[[ test_2d.pattern.image.undefined.html ]]] -->
 
 <p>Canvas test: 2d.pattern.image.undefined</p>
 <canvas id="c469" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
@@ -15216,23 +15131,17 @@ if (IsAzureEnabled()) {
 function test_2d_pattern_image_undefined() {
 
 var canvas = document.getElementById('c469');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.createPattern(undefined, 'repeat');
 } catch (e) { _thrown = e };
-if (IsAzureEnabled()) {
-  ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-} else {
-  todo(_thrown && _thrown.name == "TypeError", "should throw TypeError");
-}
-
-
+ok(_thrown && _thrown.name == "TypeError", "should throw TypeError");
 }
 </script>
 
 <!-- [[[ test_2d.pattern.modify.canvas1.html ]]] -->
 
 <p>Canvas test: 2d.pattern.modify.canvas1</p>
 <canvas id="c470" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
@@ -15722,28 +15631,20 @@ var ctx = canvas.getContext('2d');
 var img = document.getElementById('rgrg-256x256_4.png');
 var pattern = ctx.createPattern(img, 'repeat');
 ctx.fillStyle = pattern;
 ctx.fillRect(0, 0, 100, 50);
 
 ctx.translate(-128, -78);
 ctx.fillRect(128, 78, 100, 50);
 
-if (IsAzureEnabled()) {
-  isPixel(ctx, 1,1, 0,255,0,255, 0);
-  isPixel(ctx, 98,1, 0,255,0,255, 0);
-  isPixel(ctx, 1,48, 0,255,0,255, 0);
-  isPixel(ctx, 98,48, 0,255,0,255, 0);
-} else {
-  todo_isPixel(ctx, 1,1, 0,255,0,255, 0);
-  todo_isPixel(ctx, 98,1, 0,255,0,255, 0);
-  todo_isPixel(ctx, 1,48, 0,255,0,255, 0);
-  todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
-}
-
+isPixel(ctx, 1,1, 0,255,0,255, 0);
+isPixel(ctx, 98,1, 0,255,0,255, 0);
+isPixel(ctx, 1,48, 0,255,0,255, 0);
+isPixel(ctx, 98,48, 0,255,0,255, 0);
 }
 </script>
 <img src="image_rgrg-256x256.png" id="rgrg-256x256_4.png" class="resource">
 
 <!-- [[[ test_2d.pattern.paint.repeat.outside.html ]]] -->
 
 <p>Canvas test: 2d.pattern.paint.repeat.outside</p>
 <canvas id="c485" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
@@ -15826,32 +15727,22 @@ var img = document.getElementById('red-1
 var pattern = ctx.createPattern(img, 'repeat-x');
 ctx.fillStyle = pattern;
 ctx.translate(0, 16);
 ctx.fillRect(0, -16, 100, 50);
 
 ctx.fillStyle = '#0f0';
 ctx.fillRect(0, 0, 100, 16);
 
-if (IsAzureEnabled()) {
-  isPixel(ctx, 1,1, 0,255,0,255, 0);
-  isPixel(ctx, 98,1, 0,255,0,255, 0);
-  isPixel(ctx, 1,48, 0,255,0,255, 0);
-  isPixel(ctx, 98,48, 0,255,0,255, 0);
-} else {
-  todo_isPixel(ctx, 1,1, 0,255,0,255, 0);
-  todo_isPixel(ctx, 98,1, 0,255,0,255, 0);
-  todo_isPixel(ctx, 1,48, 0,255,0,255, 0);
-  todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
-}
-
+isPixel(ctx, 1,1, 0,255,0,255, 0);
+isPixel(ctx, 98,1, 0,255,0,255, 0);
+isPixel(ctx, 1,48, 0,255,0,255, 0);
+isPixel(ctx, 98,48, 0,255,0,255, 0);
 isPixel(ctx, 1,25, 0,255,0,255, 0);
 isPixel(ctx, 98,25, 0,255,0,255, 0);
-
-
 }
 </script>
 <img src="image_red-16x16.png" id="red-16x16_1.png" class="resource">
 
 <!-- [[[ test_2d.pattern.paint.repeatx.outside.html ]]] -->
 
 <p>Canvas test: 2d.pattern.paint.repeatx.outside</p>
 <canvas id="c488" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
@@ -15872,26 +15763,18 @@ var pattern = ctx.createPattern(img, 're
 ctx.fillStyle = pattern;
 ctx.fillRect(0, 0, 100, 50);
 
 ctx.fillStyle = '#0f0';
 ctx.fillRect(0, 0, 100, 16);
 
 isPixel(ctx, 1,1, 0,255,0,255, 0);
 isPixel(ctx, 98,1, 0,255,0,255, 0);
-
-if (IsAzureEnabled()) {
-  isPixel(ctx, 1,48, 0,255,0,255, 0);
-  isPixel(ctx, 98,48, 0,255,0,255, 0);
-} else {
-  todo_isPixel(ctx, 1,48, 0,255,0,255, 0);
-  todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
-}
-
-
+isPixel(ctx, 1,48, 0,255,0,255, 0);
+isPixel(ctx, 98,48, 0,255,0,255, 0);
 }
 </script>
 <img src="image_red-16x16.png" id="red-16x16_2.png" class="resource">
 
 <!-- [[[ test_2d.pattern.paint.repeaty.basic.html ]]] -->
 
 <p>Canvas test: 2d.pattern.paint.repeaty.basic</p>
 <canvas id="c489" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
@@ -15946,29 +15829,20 @@ ctx.translate(48, 0);
 ctx.fillRect(-48, 0, 100, 50);
 
 ctx.fillStyle = '#0f0';
 ctx.fillRect(0, 0, 16, 50);
 
 isPixel(ctx, 50,1, 0,255,0,255, 0);
 isPixel(ctx, 50,48, 0,255,0,255, 0);
 
-if (IsAzureEnabled()) {
-  isPixel(ctx, 1,1, 0,255,0,255, 0);
-  isPixel(ctx, 98,1, 0,255,0,255, 0);
-  isPixel(ctx, 1,48, 0,255,0,255, 0);
-  isPixel(ctx, 98,48, 0,255,0,255, 0);
-} else {
-  todo_isPixel(ctx, 1,1, 0,255,0,255, 0);
-  todo_isPixel(ctx, 98,1, 0,255,0,255, 0);
-  todo_isPixel(ctx, 1,48, 0,255,0,255, 0);
-  todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
-}
-
-
+isPixel(ctx, 1,1, 0,255,0,255, 0);
+isPixel(ctx, 98,1, 0,255,0,255, 0);
+isPixel(ctx, 1,48, 0,255,0,255, 0);
+isPixel(ctx, 98,48, 0,255,0,255, 0);
 }
 </script>
 <img src="image_red-16x16.png" id="red-16x16_3.png" class="resource">
 
 <!-- [[[ test_2d.pattern.paint.repeaty.outside.html ]]] -->
 
 <p>Canvas test: 2d.pattern.paint.repeaty.outside</p>
 <canvas id="c491" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
@@ -15989,25 +15863,18 @@ var pattern = ctx.createPattern(img, 're
 ctx.fillStyle = pattern;
 ctx.fillRect(0, 0, 100, 50);
 
 ctx.fillStyle = '#0f0';
 ctx.fillRect(0, 0, 16, 50);
 
 isPixel(ctx, 1,1, 0,255,0,255, 0);
 isPixel(ctx, 1,48, 0,255,0,255, 0);
-
-if (IsAzureEnabled()) {
-  isPixel(ctx, 98,1, 0,255,0,255, 0);
-  isPixel(ctx, 98,48, 0,255,0,255, 0);
-} else {
-  todo_isPixel(ctx, 98,1, 0,255,0,255, 0);
-  todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
-}
-
+isPixel(ctx, 98,1, 0,255,0,255, 0);
+isPixel(ctx, 98,48, 0,255,0,255, 0);
 }
 </script>
 <img src="image_red-16x16.png" id="red-16x16_4.png" class="resource">
 
 <!-- [[[ test_2d.pattern.repeat.case.html ]]] -->
 
 <p>Canvas test: 2d.pattern.repeat.case</p>
 <canvas id="c492" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
@@ -19643,21 +19510,17 @@ ok(window.CanvasRenderingContext2D.proto
 ok(window.CanvasRenderingContext2D.prototype.fill, "window.CanvasRenderingContext2D.prototype.fill");
 window.CanvasRenderingContext2D.prototype = null;
 ok(window.CanvasRenderingContext2D.prototype, "window.CanvasRenderingContext2D.prototype");
 delete window.CanvasRenderingContext2D.prototype;
 ok(window.CanvasRenderingContext2D.prototype, "window.CanvasRenderingContext2D.prototype");
 window.CanvasRenderingContext2D.prototype.fill = 1;
 ok(window.CanvasRenderingContext2D.prototype.fill === 1, "window.CanvasRenderingContext2D.prototype.fill === 1");
 delete window.CanvasRenderingContext2D.prototype.fill;
-if (IsAzureEnabled()) {
-  ok(window.CanvasRenderingContext2D.prototype.fill === undefined, "window.CanvasRenderingContext2D.prototype.fill === undefined");
-} else {
-  todo(window.CanvasRenderingContext2D.prototype.fill === undefined, "window.CanvasRenderingContext2D.prototype.fill === undefined");
-}
+ok(window.CanvasRenderingContext2D.prototype.fill === undefined, "window.CanvasRenderingContext2D.prototype.fill === undefined");
 
 //restore the original method to ensure that other tests can run successfully
 window.CanvasRenderingContext2D.prototype.fill = fill;
 }
 </script>
 
 <!-- [[[ test_2d.type.replace.html ]]] -->
 
--- a/content/canvas/test/webgl/conformance/buffers/00_test_list.txt
+++ b/content/canvas/test/webgl/conformance/buffers/00_test_list.txt
@@ -1,8 +1,7 @@
 buffer-bind-test.html
 buffer-data-array-buffer.html
 index-validation-copies-indices.html
 index-validation-crash-with-buffer-sub-data.html
 index-validation-verifies-too-many-indices.html
 index-validation-with-resized-buffer.html
-#index-validation.html # reenable me ASAP!
-
+index-validation.html
--- a/content/events/src/Makefile.in
+++ b/content/events/src/Makefile.in
@@ -56,16 +56,17 @@ CPPSRCS		= \
 		nsDOMSimpleGestureEvent.cpp \
 		nsDOMEventTargetHelper.cpp \
 		nsDOMScrollAreaEvent.cpp \
 		nsDOMTransitionEvent.cpp \
 		nsDOMAnimationEvent.cpp \
 		nsDOMTouchEvent.cpp \
 		nsDOMCompositionEvent.cpp \
 		DOMWheelEvent.cpp \
+		TextComposition.cpp \
 		$(NULL)
 
 ifdef MOZ_B2G_RIL
 CPPSRCS += \
     nsDOMWifiEvent.cpp \
     $(NULL)
 endif
 
new file mode 100644
--- /dev/null
+++ b/content/events/src/TextComposition.cpp
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextComposition.h"
+#include "nsContentEventHandler.h"
+#include "nsContentUtils.h"
+#include "nsEventDispatcher.h"
+#include "nsGUIEvent.h"
+#include "nsIMEStateManager.h"
+#include "nsIPresShell.h"
+#include "nsIWidget.h"
+#include "nsPresContext.h"
+
+namespace mozilla {
+
+/******************************************************************************
+ * TextComposition
+ ******************************************************************************/
+
+TextComposition::TextComposition(nsPresContext* aPresContext,
+                                 nsINode* aNode,
+                                 nsGUIEvent* aEvent) :
+  mPresContext(aPresContext), mNode(aNode),
+  // temporarily, we should assume that one native IME context is per native
+  // widget.
+  mNativeContext(aEvent->widget),
+  mIsSynthesizedForTests(
+    (aEvent->flags & NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT) != 0)
+{
+}
+
+TextComposition::TextComposition(const TextComposition& aOther)
+{
+  mNativeContext = aOther.mNativeContext;
+  mPresContext = aOther.mPresContext;
+  mNode = aOther.mNode;
+  mLastData = aOther.mLastData;
+  mIsSynthesizedForTests = aOther.mIsSynthesizedForTests;
+}
+
+bool
+TextComposition::MatchesNativeContext(nsIWidget* aWidget) const
+{
+  // temporarily, we should assume that one native IME context is per one
+  // native widget.
+  return mNativeContext == static_cast<void*>(aWidget);
+}
+
+bool
+TextComposition::MatchesEventTarget(nsPresContext* aPresContext,
+                                    nsINode* aNode) const
+{
+  return mPresContext == aPresContext && mNode == aNode;
+}
+
+void
+TextComposition::DispatchEvent(nsGUIEvent* aEvent,
+                               nsEventStatus* aStatus,
+                               nsDispatchingCallback* aCallBack)
+{
+  if (aEvent->message == NS_COMPOSITION_UPDATE) {
+    mLastData = static_cast<nsCompositionEvent*>(aEvent)->data;
+  }
+
+  nsEventDispatcher::Dispatch(mNode, mPresContext,
+                              aEvent, nullptr, aStatus, aCallBack);
+}
+
+void
+TextComposition::DispatchCompsotionEventRunnable(uint32_t aEventMessage,
+                                                 const nsAString& aData)
+{
+  nsContentUtils::AddScriptRunner(
+    new CompositionEventDispatcher(mPresContext, mNode,
+                                   aEventMessage, aData));
+}
+
+void
+TextComposition::SynthesizeCommit(bool aDiscard)
+{
+  // backup this instance and use it since this instance might be destroyed
+  // by nsIMEStateManager if this is managed by it.
+  TextComposition composition = *this;
+  nsAutoString data(aDiscard ? EmptyString() : composition.mLastData);
+  if (composition.mLastData != data) {
+    composition.DispatchCompsotionEventRunnable(NS_COMPOSITION_UPDATE, data);
+    composition.DispatchCompsotionEventRunnable(NS_TEXT_TEXT, data);
+  }
+  composition.DispatchCompsotionEventRunnable(NS_COMPOSITION_END, data);
+}
+
+nsresult
+TextComposition::NotifyIME(widget::NotificationToIME aNotification)
+{
+  NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
+  return nsIMEStateManager::NotifyIME(aNotification, mPresContext);
+}
+
+/******************************************************************************
+ * TextComposition::CompositionEventDispatcher
+ ******************************************************************************/
+
+TextComposition::CompositionEventDispatcher::CompositionEventDispatcher(
+                                               nsPresContext* aPresContext,
+                                               nsINode* aEventTarget,
+                                               uint32_t aEventMessage,
+                                               const nsAString& aData) :
+  mPresContext(aPresContext), mEventTarget(aEventTarget),
+  mEventMessage(aEventMessage), mData(aData)
+{
+  mWidget = mPresContext->GetNearestWidget();
+}
+
+NS_IMETHODIMP
+TextComposition::CompositionEventDispatcher::Run()
+{
+  if (!mPresContext->GetPresShell() ||
+      mPresContext->GetPresShell()->IsDestroying()) {
+    return NS_OK; // cannot dispatch any events anymore
+  }
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+  switch (mEventMessage) {
+    case NS_COMPOSITION_START: {
+      nsCompositionEvent compStart(true, NS_COMPOSITION_START, mWidget);
+      nsQueryContentEvent selectedText(true, NS_QUERY_SELECTED_TEXT, mWidget);
+      nsContentEventHandler handler(mPresContext);
+      handler.OnQuerySelectedText(&selectedText);
+      NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text");
+      compStart.data = selectedText.mReply.mString;
+      nsIMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
+                                                  &compStart, &status, nullptr);
+      break;
+    }
+    case NS_COMPOSITION_UPDATE:
+    case NS_COMPOSITION_END: {
+      nsCompositionEvent compEvent(true, mEventMessage, mWidget);
+      compEvent.data = mData;
+      nsIMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
+                                                  &compEvent, &status, nullptr);
+      break;
+    }
+    case NS_TEXT_TEXT: {
+      nsTextEvent textEvent(true, NS_TEXT_TEXT, mWidget);
+      textEvent.theText = mData;
+      nsIMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
+                                                  &textEvent, &status, nullptr);
+      break;
+    }
+    default:
+      MOZ_NOT_REACHED("Unsupported event");
+      break;
+  }
+  return NS_OK;
+}
+
+/******************************************************************************
+ * TextCompositionArray
+ ******************************************************************************/
+
+TextCompositionArray::index_type
+TextCompositionArray::IndexOf(nsIWidget* aWidget)
+{
+  for (index_type i = Length(); i > 0; --i) {
+    if (ElementAt(i - 1).MatchesNativeContext(aWidget)) {
+      return i - 1;
+    }
+  }
+  return NoIndex;
+}
+
+TextCompositionArray::index_type
+TextCompositionArray::IndexOf(nsPresContext* aPresContext)
+{
+  for (index_type i = Length(); i > 0; --i) {
+    if (ElementAt(i - 1).GetPresContext() == aPresContext) {
+      return i - 1;
+    }
+  }
+  return NoIndex;
+}
+
+TextCompositionArray::index_type
+TextCompositionArray::IndexOf(nsPresContext* aPresContext,
+                              nsINode* aNode)
+{
+  index_type index = IndexOf(aPresContext);
+  if (index == NoIndex) {
+    return NoIndex;
+  }
+  nsINode* node = ElementAt(index).GetEventTargetNode();
+  return node == aNode ? index : NoIndex;
+}
+
+TextComposition*
+TextCompositionArray::GetCompositionFor(nsIWidget* aWidget)
+{
+  index_type i = IndexOf(aWidget);
+  return i != NoIndex ? &ElementAt(i) : nullptr;
+}
+
+TextComposition*
+TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext)
+{
+  index_type i = IndexOf(aPresContext);
+  return i != NoIndex ? &ElementAt(i) : nullptr;
+}
+
+TextComposition*
+TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext,
+                                           nsINode* aNode)
+{
+  index_type i = IndexOf(aPresContext, aNode);
+  return i != NoIndex ? &ElementAt(i) : nullptr;
+}
+
+TextComposition*
+TextCompositionArray::GetCompositionInContent(nsPresContext* aPresContext,
+                                              nsIContent* aContent)
+{
+  // There should be only one composition per content object.
+  for (index_type i = Length(); i > 0; --i) {
+    nsINode* node = ElementAt(i - 1).GetEventTargetNode();
+    if (node && nsContentUtils::ContentIsDescendantOf(node, aContent)) {
+      return &ElementAt(i - 1);
+    }
+  }
+  return nullptr;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/events/src/TextComposition.h
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et 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/. */
+
+#ifndef mozilla_TextComposition_h
+#define mozilla_TextComposition_h
+
+#include "nsCOMPtr.h"
+#include "nsEvent.h"
+#include "nsINode.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Attributes.h"
+
+class nsCompositionEvent;
+class nsDispatchingCallback;
+class nsIMEStateManager;
+class nsIWidget;
+class nsPresContext;
+
+namespace mozilla {
+
+/**
+ * TextComposition represents a text composition.  This class stores the
+ * composition event target and its presContext.  At dispatching the event via
+ * this class, the instances use the stored event target.
+ */
+
+class TextComposition MOZ_FINAL
+{
+  friend class ::nsIMEStateManager;
+public:
+  TextComposition(nsPresContext* aPresContext,
+                  nsINode* aNode,
+                  nsGUIEvent* aEvent);
+
+  TextComposition(const TextComposition& aOther);
+
+  ~TextComposition()
+  {
+    // WARNING: mPresContext may be destroying, so, be careful if you touch it.
+  }
+
+  nsPresContext* GetPresContext() const { return mPresContext; }
+  nsINode* GetEventTargetNode() const { return mNode; }
+  // The latest CompositionEvent.data value except compositionstart event.
+  const nsString& GetLastData() const { return mLastData; }
+  // Returns true if the composition is started with synthesized event which
+  // came from nsDOMWindowUtils.
+  bool IsSynthesizedForTests() const { return mIsSynthesizedForTests; }
+
+  bool MatchesNativeContext(nsIWidget* aWidget) const;
+  bool MatchesEventTarget(nsPresContext* aPresContext,
+                          nsINode* aNode) const;
+
+  /**
+   * SynthesizeCommit() dispatches compositionupdate, text and compositionend
+   * events for emulating commit on the content.
+   *
+   * @param aDiscard true when committing with empty string.  Otherwise, false.
+   */
+  void SynthesizeCommit(bool aDiscard);
+
+  /**
+   * Send a notification to IME.  It depends on the IME or platform spec what
+   * will occur (or not occur).
+   */
+  nsresult NotifyIME(widget::NotificationToIME aNotification);
+
+private:
+  // This class holds nsPresContext weak.  This instance shouldn't block
+  // destroying it.  When the presContext is being destroyed, it's notified to
+  // nsIMEStateManager::OnDestroyPresContext(), and then, it destroy
+  // this instance.
+  nsPresContext* mPresContext;
+  nsCOMPtr<nsINode> mNode;
+
+  // mNativeContext stores a opaque pointer.  This works as the "ID" for this
+  // composition.  Don't access the instance, it may not be available.
+  void* mNativeContext;
+
+  // mLastData stores the data attribute of the latest composition event (except
+  // the compositionstart event).
+  nsString mLastData;
+
+  // See the comment for IsSynthesizedForTests().
+  bool mIsSynthesizedForTests;
+
+  // Hide the default constructor
+  TextComposition() {}
+
+  /**
+   * DispatchEvent() dispatches the aEvent to the mContent synchronously.
+   * The caller must ensure that it's safe to dispatch the event.
+   */
+  void DispatchEvent(nsGUIEvent* aEvent,
+                     nsEventStatus* aStatus,
+                     nsDispatchingCallback* aCallBack);
+
+  /**
+   * CompositionEventDispatcher dispatches the specified composition (or text)
+   * event.
+   */
+  class CompositionEventDispatcher : public nsRunnable
+  {
+  public:
+    CompositionEventDispatcher(nsPresContext* aPresContext,
+                               nsINode* aEventTarget,
+                               uint32_t aEventMessage,
+                               const nsAString& aData);
+    NS_IMETHOD Run();
+
+  private:
+    nsRefPtr<nsPresContext> mPresContext;
+    nsCOMPtr<nsINode> mEventTarget;
+    nsCOMPtr<nsIWidget> mWidget;
+    uint32_t mEventMessage;
+    nsString mData;
+
+    CompositionEventDispatcher() {};
+  };
+
+  /**
+   * DispatchCompsotionEventRunnable() dispatches a composition or text event
+   * to the content.  Be aware, if you use this method, nsPresShellEventCB
+   * isn't used.  That means that nsIFrame::HandleEvent() is never called.
+   * WARNING: The instance which is managed by nsIMEStateManager may be
+   *          destroyed by this method call.
+   *
+   * @param aEventMessage       Must be one of composition event or text event.
+   * @param aData               Used for data value if aEventMessage is
+   *                            NS_COMPOSITION_UPDATE or NS_COMPOSITION_END.
+   *                            Used for theText value if aEventMessage is
+   *                            NS_TEXT_TEXT.
+   */
+  void DispatchCompsotionEventRunnable(uint32_t aEventMessage,
+                                       const nsAString& aData);
+};
+
+/**
+ * TextCompositionArray manages the instances of TextComposition class.
+ * Managing with array is enough because only one composition is typically
+ * there.  Even if user switches native IME context, it's very rare that
+ * second or more composition is started.
+ * It's assumed that this is used by nsIMEStateManager for storing all active
+ * compositions in the process.  If the instance is it, each TextComposition
+ * in the array can be destroyed by calling some methods of itself.
+ */
+
+class TextCompositionArray MOZ_FINAL : public nsAutoTArray<TextComposition, 2>
+{
+public:
+  index_type IndexOf(nsIWidget* aWidget);
+  index_type IndexOf(nsPresContext* aPresContext);
+  index_type IndexOf(nsPresContext* aPresContext, nsINode* aNode);
+
+  TextComposition* GetCompositionFor(nsIWidget* aWidget);
+  TextComposition* GetCompositionFor(nsPresContext* aPresContext);
+  TextComposition* GetCompositionFor(nsPresContext* aPresContext,
+                                     nsINode* aNode);
+  TextComposition* GetCompositionInContent(nsPresContext* aPresContext,
+                                           nsIContent* aContent);
+};
+
+} // namespace mozilla
+
+#endif // #ifndef mozilla_TextComposition_h
--- a/content/events/src/nsDOMEventTargetHelper.cpp
+++ b/content/events/src/nsDOMEventTargetHelper.cpp
@@ -42,17 +42,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mListenerManager)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMEventTargetHelper)
   if (tmp->IsBlack()) {
     if (tmp->mListenerManager) {
-      tmp->mListenerManager->UnmarkGrayJSListeners();
+      tmp->mListenerManager->MarkForCC();
     }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDOMEventTargetHelper)
   return tmp->IsBlackAndDoesNotNeedTracing(tmp);
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
--- a/content/events/src/nsDOMEventTargetHelper.h
+++ b/content/events/src/nsDOMEventTargetHelper.h
@@ -28,17 +28,17 @@ public:
   nsDOMEventTargetHelper() : mOwner(nullptr), mHasOrHasHadOwner(false) {}
   virtual ~nsDOMEventTargetHelper();
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsDOMEventTargetHelper)
 
   NS_DECL_NSIDOMEVENTTARGET
   void AddEventListener(const nsAString& aType,
                         nsIDOMEventListener* aCallback, // XXX nullable
-                        bool aCapture, Nullable<bool>& aWantsUntrusted,
+                        bool aCapture, const Nullable<bool>& aWantsUntrusted,
                         mozilla::ErrorResult& aRv)
   {
     aRv = AddEventListener(aType, aCallback, aCapture,
                            !aWantsUntrusted.IsNull() && aWantsUntrusted.Value(),
                            aWantsUntrusted.IsNull() ? 1 : 2);
   }
   void RemoveEventListener(const nsAString& aType,
                            nsIDOMEventListener* aCallback,
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -1174,22 +1174,25 @@ nsEventListenerManager::SizeOfIncludingT
     if (jsl) {
       n += jsl->SizeOfIncludingThis(aMallocSizeOf);
     }
   }
   return n;
 }
 
 void
-nsEventListenerManager::UnmarkGrayJSListeners()
+nsEventListenerManager::MarkForCC()
 {
   uint32_t count = mListeners.Length();
   for (uint32_t i = 0; i < count; ++i) {
     const nsListenerStruct& ls = mListeners.ElementAt(i);
     nsIJSEventListener* jsl = ls.GetJSListener();
     if (jsl) {
       xpc_UnmarkGrayObject(jsl->GetHandler());
       xpc_UnmarkGrayObject(jsl->GetEventScope());
     } else if (ls.mListenerType == eWrappedJSListener) {
       xpc_TryUnmarkWrappedGrayObject(ls.mListener);
     }
   }
+  if (mRefCnt.IsPurple()) {
+    mRefCnt.RemovePurple();
+  }
 }
--- a/content/events/src/nsEventListenerManager.h
+++ b/content/events/src/nsEventListenerManager.h
@@ -221,17 +221,17 @@ public:
    * false if there definitely isn't.
    */
   bool MayHaveTouchEventListener() { return mMayHaveTouchEventListener; }
 
   bool MayHaveMouseEnterLeaveEventListener() { return mMayHaveMouseEnterLeaveEventListener; }
 
   size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
 
-  void UnmarkGrayJSListeners();
+  void MarkForCC();
 
   nsISupports* GetTarget() { return mTarget; }
 protected:
   nsresult HandleEventSubType(nsListenerStruct* aListenerStruct,
                               nsIDOMEventListener* aListener,
                               nsIDOMEvent* aDOMEvent,
                               nsIDOMEventTarget* aCurrentTarget,
                               uint32_t aPhaseFlags,
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -4881,16 +4881,18 @@ nsEventStateManager::ContentRemoved(nsID
   if (aContent->IsHTML() &&
       (aContent->Tag() == nsGkAtoms::a || aContent->Tag() == nsGkAtoms::area) &&
       (aContent->AsElement()->State().HasAtLeastOneOfStates(NS_EVENT_STATE_FOCUS |
                                                             NS_EVENT_STATE_HOVER))) {
     nsGenericHTMLElement* element = static_cast<nsGenericHTMLElement*>(aContent);
     element->LeaveLink(element->GetPresContext());
   }
 
+  nsIMEStateManager::OnRemoveContent(mPresContext, aContent);
+
   // inform the focus manager that the content is being removed. If this
   // content is focused, the focus will be removed without firing events.
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
   if (fm)
     fm->ContentRemoved(aDocument, aContent);
 
   if (mHoverContent &&
       nsContentUtils::ContentIsDescendantOf(mHoverContent, aContent)) {
--- a/content/events/src/nsIMEStateManager.cpp
+++ b/content/events/src/nsIMEStateManager.cpp
@@ -29,34 +29,59 @@
 #include "nsIMutationObserver.h"
 #include "nsContentEventHandler.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "nsIFormControl.h"
 #include "nsIForm.h"
 #include "nsHTMLFormElement.h"
 #include "mozilla/Attributes.h"
+#include "nsEventDispatcher.h"
+#include "TextComposition.h"
 
+using namespace mozilla;
 using namespace mozilla::widget;
 
 /******************************************************************/
 /* nsIMEStateManager                                              */
 /******************************************************************/
 
 nsIContent*    nsIMEStateManager::sContent      = nullptr;
 nsPresContext* nsIMEStateManager::sPresContext  = nullptr;
 bool           nsIMEStateManager::sInstalledMenuKeyboardListener = false;
 bool           nsIMEStateManager::sInSecureInputMode = false;
 
 nsTextStateManager* nsIMEStateManager::sTextStateObserver = nullptr;
+TextCompositionArray* nsIMEStateManager::sTextCompositions = nullptr;
+
+void
+nsIMEStateManager::Shutdown()
+{
+  MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
+  delete sTextCompositions;
+  sTextCompositions = nullptr;
+}
 
 nsresult
 nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
 {
   NS_ENSURE_ARG_POINTER(aPresContext);
+
+  // First, if there is a composition in the aPresContext, clean up it.
+  if (sTextCompositions) {
+    TextCompositionArray::index_type i =
+      sTextCompositions->IndexOf(aPresContext);
+    if (i != TextCompositionArray::NoIndex) {
+      // there should be only one composition per presContext object.
+      sTextCompositions->RemoveElementAt(i);
+      MOZ_ASSERT(sTextCompositions->IndexOf(aPresContext) ==
+                   TextCompositionArray::NoIndex);
+    }
+  }
+
   if (aPresContext != sPresContext)
     return NS_OK;
   nsCOMPtr<nsIWidget> widget = GetWidget(sPresContext);
   if (widget) {
     IMEState newState = GetNewIMEState(sPresContext, nullptr);
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::LOST_FOCUS);
     SetIMEState(newState, nullptr, widget, action);
@@ -67,27 +92,59 @@ nsIMEStateManager::OnDestroyPresContext(
   return NS_OK;
 }
 
 nsresult
 nsIMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
                                    nsIContent* aContent)
 {
   NS_ENSURE_ARG_POINTER(aPresContext);
+
+  // First, if there is a composition in the aContent, clean up it.
+  if (sTextCompositions) {
+    TextComposition* compositionInContent =
+      sTextCompositions->GetCompositionInContent(aPresContext, aContent);
+
+    if (compositionInContent) {
+      // Store the composition before accessing the native IME.
+      TextComposition storedComposition = *compositionInContent;
+      // Try resetting the native IME state.  Be aware, typically, this method
+      // is called during the content being removed.  Then, the native
+      // composition events which are caused by following APIs are ignored due
+      // to unsafe to run script (in PresShell::HandleEvent()).
+      nsCOMPtr<nsIWidget> widget = aPresContext->GetNearestWidget();
+      if (widget) {
+        nsresult rv =
+          storedComposition.NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
+        if (NS_FAILED(rv)) {
+          storedComposition.NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
+        }
+        // By calling the APIs, the composition may have been finished normally.
+        compositionInContent =
+          sTextCompositions->GetCompositionFor(
+                               storedComposition.GetPresContext(),
+                               storedComposition.GetEventTargetNode());
+      }
+    }
+
+    // If the compositionInContent is still available, we should finish the
+    // composition just on the content forcibly.
+    if (compositionInContent) {
+      compositionInContent->SynthesizeCommit(true);
+    }
+  }
+
   if (!sPresContext || !sContent ||
-      aPresContext != sPresContext ||
-      aContent != sContent)
+      !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) {
     return NS_OK;
+  }
 
   // Current IME transaction should commit
   nsCOMPtr<nsIWidget> widget = GetWidget(sPresContext);
   if (widget) {
-    nsresult rv = widget->CancelIMEComposition();
-    if (NS_FAILED(rv))
-      widget->ResetInputState();
     IMEState newState = GetNewIMEState(sPresContext, nullptr);
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::LOST_FOCUS);
     SetIMEState(newState, nullptr, widget, action);
   }
 
   NS_IF_RELEASE(sContent);
   sPresContext = nullptr;
@@ -159,18 +216,19 @@ nsIMEStateManager::OnChangeFocusInternal
 
   // Current IME transaction should commit
   if (sPresContext) {
     nsCOMPtr<nsIWidget> oldWidget;
     if (sPresContext == aPresContext)
       oldWidget = widget;
     else
       oldWidget = GetWidget(sPresContext);
-    if (oldWidget)
-      oldWidget->ResetInputState();
+    if (oldWidget) {
+      NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
+    }
   }
 
   // Update IME state for new focus widget
   SetIMEState(newState, aContent, widget, aAction);
 
   sPresContext = aPresContext;
   if (sContent != aContent) {
     NS_IF_RELEASE(sContent);
@@ -246,17 +304,17 @@ nsIMEStateManager::UpdateIMEState(const 
 
   // Don't update IME state when enabled state isn't actually changed.
   InputContext context = widget->GetInputContext();
   if (context.mIMEState.mEnabled == aNewIMEState.mEnabled) {
     return;
   }
 
   // commit current composition
-  widget->ResetInputState();
+  NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget);
 
   InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                             InputContextAction::FOCUS_NOT_CHANGED);
   SetIMEState(aNewIMEState, aContent, widget, action);
 }
 
 IMEState
 nsIMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
@@ -379,16 +437,176 @@ nsIMEStateManager::GetWidget(nsPresConte
   if (!vm)
     return nullptr;
   nsCOMPtr<nsIWidget> widget = nullptr;
   nsresult rv = vm->GetRootWidget(getter_AddRefs(widget));
   NS_ENSURE_SUCCESS(rv, nullptr);
   return widget;
 }
 
+void
+nsIMEStateManager::EnsureTextCompositionArray()
+{
+  if (sTextCompositions) {
+    return;
+  }
+  sTextCompositions = new TextCompositionArray();
+}
+
+void
+nsIMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode,
+                                            nsPresContext* aPresContext,
+                                            nsEvent* aEvent,
+                                            nsEventStatus* aStatus,
+                                            nsDispatchingCallback* aCallBack)
+{
+  MOZ_ASSERT(aEvent->eventStructType == NS_COMPOSITION_EVENT ||
+             aEvent->eventStructType == NS_TEXT_EVENT);
+  if (!NS_IS_TRUSTED_EVENT(aEvent) ||
+      (aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) != 0) {
+    return;
+  }
+
+  EnsureTextCompositionArray();
+
+  nsGUIEvent* GUIEvent = static_cast<nsGUIEvent*>(aEvent);
+
+  TextComposition* composition =
+    sTextCompositions->GetCompositionFor(GUIEvent->widget);
+  if (!composition) {
+    MOZ_ASSERT(GUIEvent->message == NS_COMPOSITION_START);
+    TextComposition newComposition(aPresContext, aEventTargetNode, GUIEvent);
+    composition = sTextCompositions->AppendElement(newComposition);
+  }
+#ifdef DEBUG
+  else {
+    MOZ_ASSERT(GUIEvent->message != NS_COMPOSITION_START);
+  }
+#endif // #ifdef DEBUG
+
+  // Dispatch the event on composing target.
+  composition->DispatchEvent(GUIEvent, aStatus, aCallBack);
+
+  // WARNING: the |composition| might have been destroyed already.
+
+  // Remove the ended composition from the array.
+  if (aEvent->message == NS_COMPOSITION_END) {
+    TextCompositionArray::index_type i =
+      sTextCompositions->IndexOf(GUIEvent->widget);
+    if (i != TextCompositionArray::NoIndex) {
+      sTextCompositions->RemoveElementAt(i);
+    }
+  }
+}
+
+// static
+nsresult
+nsIMEStateManager::NotifyIME(NotificationToIME aNotification,
+                             nsIWidget* aWidget)
+{
+  NS_ENSURE_TRUE(aWidget, NS_ERROR_INVALID_ARG);
+
+  TextComposition* composition = nullptr;
+  if (sTextCompositions) {
+    composition = sTextCompositions->GetCompositionFor(aWidget);
+  }
+  if (!composition || !composition->IsSynthesizedForTests()) {
+    switch (aNotification) {
+      case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
+        return aWidget->ResetInputState();
+      case REQUEST_TO_COMMIT_COMPOSITION:
+        return composition ? aWidget->ResetInputState() : NS_OK;
+      case REQUEST_TO_CANCEL_COMPOSITION:
+        return composition ? aWidget->CancelIMEComposition() : NS_OK;
+      default:
+        MOZ_NOT_REACHED("Unsupported notification");
+        return NS_ERROR_INVALID_ARG;
+    }
+    MOZ_NOT_REACHED(
+      "Failed to handle the notification for non-synthesized composition");
+  }
+
+  // If the composition is synthesized events for automated tests, we should
+  // dispatch composition events for emulating the native composition behavior.
+  // NOTE: The dispatched events are discarded if it's not safe to run script.
+  switch (aNotification) {
+    case REQUEST_TO_COMMIT_COMPOSITION: {
+      nsCOMPtr<nsIWidget> widget(aWidget);
+      TextComposition backup = *composition;
+
+      nsEventStatus status = nsEventStatus_eIgnore;
+      if (!backup.GetLastData().IsEmpty()) {
+        nsTextEvent textEvent(true, NS_TEXT_TEXT, widget);
+        textEvent.theText = backup.GetLastData();
+        textEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+        widget->DispatchEvent(&textEvent, status);
+        if (widget->Destroyed()) {
+          return NS_OK;
+        }
+      }
+
+      status = nsEventStatus_eIgnore;
+      nsCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
+      endEvent.data = backup.GetLastData();
+      endEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+      widget->DispatchEvent(&endEvent, status);
+
+      return NS_OK;
+    }
+    case REQUEST_TO_CANCEL_COMPOSITION: {
+      nsCOMPtr<nsIWidget> widget(aWidget);
+      TextComposition backup = *composition;
+
+      nsEventStatus status = nsEventStatus_eIgnore;
+      if (!backup.GetLastData().IsEmpty()) {
+        nsCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE, widget);
+        updateEvent.data = backup.GetLastData();
+        updateEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+        widget->DispatchEvent(&updateEvent, status);
+        if (widget->Destroyed()) {
+          return NS_OK;
+        }
+
+        status = nsEventStatus_eIgnore;
+        nsTextEvent textEvent(true, NS_TEXT_TEXT, widget);
+        textEvent.theText = backup.GetLastData();
+        textEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+        widget->DispatchEvent(&textEvent, status);
+        if (widget->Destroyed()) {
+          return NS_OK;
+        }
+      }
+
+      status = nsEventStatus_eIgnore;
+      nsCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
+      endEvent.data = backup.GetLastData();
+      endEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+      widget->DispatchEvent(&endEvent, status);
+
+      return NS_OK;
+    }
+    default:
+      return NS_OK;
+  }
+}
+
+// static
+nsresult
+nsIMEStateManager::NotifyIME(NotificationToIME aNotification,
+                             nsPresContext* aPresContext)
+{
+  NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG);
+
+  nsIWidget* widget = aPresContext->GetNearestWidget();
+  if (!widget) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  return NotifyIME(aNotification, widget);
+}
+
 
 // nsTextStateManager notifies widget of any text and selection changes
 //  in the currently focused editor
 // sTextStateObserver points to the currently active nsTextStateManager
 // sTextStateObserver is null if there is no focused editor
 
 class nsTextStateManager MOZ_FINAL : public nsISelectionListener,
                                      public nsStubMutationObserver
--- a/content/events/src/nsIMEStateManager.h
+++ b/content/events/src/nsIMEStateManager.h
@@ -2,37 +2,46 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsIMEStateManager_h__
 #define nsIMEStateManager_h__
 
 #include "nscore.h"
+#include "nsEvent.h"
 #include "nsIWidget.h"
 
+class nsDispatchingCallback;
 class nsIContent;
 class nsIDOMMouseEvent;
+class nsINode;
 class nsPIDOMWindow;
 class nsPresContext;
 class nsTextStateManager;
 class nsISelection;
 
+namespace mozilla {
+class TextCompositionArray;
+} // namespace mozilla
+
 /*
  * IME state manager
  */
 
 class nsIMEStateManager
 {
 protected:
   typedef mozilla::widget::IMEState IMEState;
   typedef mozilla::widget::InputContext InputContext;
   typedef mozilla::widget::InputContextAction InputContextAction;
 
 public:
+  static void Shutdown();
+
   static nsresult OnDestroyPresContext(nsPresContext* aPresContext);
   static nsresult OnRemoveContent(nsPresContext* aPresContext,
                                   nsIContent* aContent);
   /**
    * OnChangeFocus() should be called when focused content is changed or
    * IME enabled state is changed.  If focus isn't actually changed and IME
    * enabled state isn't changed, this will do nothing.
    */
@@ -70,30 +79,59 @@ public:
   // aContent must be:
   //   If the editor is for <input> or <textarea>, the element.
   //   If the editor is for contenteditable, the active editinghost.
   //   If the editor is for designMode, NULL.
   static void OnClickInEditor(nsPresContext* aPresContext,
                               nsIContent* aContent,
                               nsIDOMMouseEvent* aMouseEvent);
 
+  /**
+   * All DOM composition events and DOM text events must be dispatched via
+   * DispatchCompositionEvent() for storing the composition target
+   * and ensuring a set of composition events must be fired the stored target.
+   * If the stored composition event target is destroying, this removes the
+   * stored composition automatically.
+   */
+  static void DispatchCompositionEvent(nsINode* aEventTargetNode,
+                                       nsPresContext* aPresContext,
+                                       nsEvent* aEvent,
+                                       nsEventStatus* aStatus,
+                                       nsDispatchingCallback* aCallBack);
+
+  /**
+   * Send a notification to IME.  It depends on the IME or platform spec what
+   * will occur (or not occur).
+   */
+  static nsresult NotifyIME(mozilla::widget::NotificationToIME aNotification,
+                            nsIWidget* aWidget);
+  static nsresult NotifyIME(mozilla::widget::NotificationToIME aNotification,
+                            nsPresContext* aPresContext);
+
 protected:
   static nsresult OnChangeFocusInternal(nsPresContext* aPresContext,
                                         nsIContent* aContent,
                                         InputContextAction aAction);
   static void SetIMEState(const IMEState &aState,
                           nsIContent* aContent,
                           nsIWidget* aWidget,
                           InputContextAction aAction);
   static IMEState GetNewIMEState(nsPresContext* aPresContext,
                                  nsIContent* aContent);
 
   static nsIWidget* GetWidget(nsPresContext* aPresContext);
+  static void EnsureTextCompositionArray();
 
   static nsIContent*    sContent;
   static nsPresContext* sPresContext;
   static bool           sInstalledMenuKeyboardListener;
   static bool           sInSecureInputMode;
 
   static nsTextStateManager* sTextStateObserver;
+
+  // All active compositions in the process are stored by this array.
+  // When you get an item of this array and use it, please be careful.
+  // The instances in this array can be destroyed automatically if you do
+  // something to cause committing or canceling the composition.
+  static mozilla::TextCompositionArray* sTextCompositions;
 };
 
 #endif // nsIMEStateManager_h__
--- a/content/html/content/src/nsDOMStringMap.cpp
+++ b/content/html/content/src/nsDOMStringMap.cpp
@@ -10,28 +10,35 @@
 #include "nsDOMClassInfoID.h"
 #include "nsGenericHTMLElement.h"
 #include "nsContentUtils.h"
 
 DOMCI_DATA(DOMStringMap, nsDOMStringMap)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStringMap)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMStringMap)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mElement)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStringMap)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   // Check that mElement exists in case the unlink code is run more than once.
   if (tmp->mElement) {
     // Call back to element to null out weak reference to this object.
     tmp->mElement->ClearDataset();
     tmp->mElement = nullptr;
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMStringMap)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStringMap)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIDOMDOMStringMap)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DOMStringMap)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMStringMap)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMStringMap)
 
--- a/content/html/content/src/nsDOMStringMap.h
+++ b/content/html/content/src/nsDOMStringMap.h
@@ -8,26 +8,52 @@
 #define nsDOMStringMap_h
 
 #include "nsIDOMDOMStringMap.h"
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "nsString.h"
+#include "nsWrapperCache.h"
+#include "nsGenericHTMLElement.h"
 
-class nsGenericHTMLElement;
-
-class nsDOMStringMap : public nsIDOMDOMStringMap
+class nsDOMStringMap : public nsIDOMDOMStringMap,
+                       public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIDOMDOMSTRINGMAP
-  NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMStringMap)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMStringMap)
+
+  nsINode* GetParentObject()
+  {
+    return mElement;
+  }
 
+  static nsDOMStringMap* FromSupports(nsISupports* aSupports)
+  {
+    nsIDOMDOMStringMap* map =
+      static_cast<nsDOMStringMap*>(aSupports);
+#ifdef DEBUG
+    {
+      nsCOMPtr<nsIDOMDOMStringMap> map_qi =
+        do_QueryInterface(aSupports);
+
+      // If this assertion fires the QI implementation for the object in
+      // question doesn't use the nsIDOMDOMStringMap pointer as the
+      // nsISupports pointer. That must be fixed, or we'll crash...
+      NS_ASSERTION(map_qi == map, "Uh, fix QI!");
+    }
+#endif
+
+    return static_cast<nsDOMStringMap*>(map);
+  }
+
+  
   nsDOMStringMap(nsGenericHTMLElement* aElement);
 
   // GetDataPropList is not defined in IDL due to difficulty
   // of returning arrays in IDL. Instead, we cast to this
   // class if this method needs to be called.
   nsresult GetDataPropList(nsTArray<nsString>& aResult);
 
   nsresult RemovePropInternal(nsIAtom* aAttr);
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -339,28 +339,35 @@ nsGenericHTMLElement::SetAttribute(const
 
     return SetAttr(kNameSpaceID_None, nameAtom, aValue, true);
   }
 
   return SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(),
                  aValue, true);
 }
 
-nsresult
-nsGenericHTMLElement::GetDataset(nsIDOMDOMStringMap** aDataset)
+already_AddRefed<nsDOMStringMap>
+nsGenericHTMLElement::Dataset()
 {
   nsDOMSlots *slots = DOMSlots();
 
   if (!slots->mDataset) {
     // mDataset is a weak reference so assignment will not AddRef.
-    // AddRef is called before assigning to out parameter.
+    // AddRef is called before returning the pointer.
     slots->mDataset = new nsDOMStringMap(this);
   }
 
-  NS_ADDREF(*aDataset = slots->mDataset);
+  NS_ADDREF(slots->mDataset);
+  return slots->mDataset;
+}
+
+nsresult
+nsGenericHTMLElement::GetDataset(nsIDOMDOMStringMap** aDataset)
+{
+  *aDataset = Dataset().get();
   return NS_OK;
 }
 
 nsresult
 nsGenericHTMLElement::ClearDataset()
 {
   nsDOMSlots *slots = GetExistingDOMSlots();
 
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -214,16 +214,17 @@ public:
   NS_IMETHOD GetProperties(nsIDOMHTMLPropertiesCollection** aReturn);
   NS_IMETHOD GetAccessKey(nsAString &aAccessKey);
   NS_IMETHOD SetAccessKey(const nsAString& aAccessKey);
   NS_IMETHOD GetAccessKeyLabel(nsAString& aLabel);
   nsresult GetContentEditable(nsAString& aContentEditable);
   nsresult GetIsContentEditable(bool* aContentEditable);
   nsresult SetContentEditable(const nsAString &aContentEditable);
   nsresult GetDataset(nsIDOMDOMStringMap** aDataset);
+  already_AddRefed<nsDOMStringMap> Dataset();
   // Callback for destructor of of dataset to ensure to null out weak pointer.
   nsresult ClearDataset();
   nsresult GetContextMenu(nsIDOMHTMLMenuElement** aContextMenu);
 
   /**
    * Get width and height, using given image request if attributes are unset.
    */
   nsSize GetWidthHeightForImage(imgIRequest *aImageRequest);
--- a/content/media/webrtc/MediaEngine.h
+++ b/content/media/webrtc/MediaEngine.h
@@ -68,21 +68,36 @@ public:
   virtual nsresult Start(SourceMediaStream*, TrackID) = 0;
 
   /* Take a snapshot from this source. In the case of video this is a single
    * image, and for audio, it is a snippet lasting aDuration milliseconds. The
    * duration argument is ignored for a MediaEngineVideoSource.
    */
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile) = 0;
 
+  /* Called when the stream wants more data */
+  virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) = 0;
+
   /* Stop the device and release the corresponding MediaStream */
   virtual nsresult Stop() = 0;
 
+  /* Return false if device is currently allocated or started */
+  bool IsAvailable() {
+    if (mState == kAllocated || mState == kStarted) {
+      return false;
+    } else {
+      return true;
+    }
+  }
+
   /* It is an error to call Start() before an Allocate(), and Stop() before
    * a Start(). Only Allocate() may be called after a Deallocate(). */
+
+protected:
+  MediaEngineState mState;
 };
 
 /**
  * Video source and friends.
  */
 enum MediaEngineVideoCodecType {
   kVideoCodecH263,
   kVideoCodecVP8,
--- a/content/media/webrtc/MediaEngineDefault.cpp
+++ b/content/media/webrtc/MediaEngineDefault.cpp
@@ -30,18 +30,20 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(MediaEngin
 const MediaEngineVideoOptions MediaEngineDefaultVideoSource::mOpts = {
   DEFAULT_WIDTH,
   DEFAULT_HEIGHT,
   DEFAULT_FPS,
   kVideoCodecI420
 };
 
 MediaEngineDefaultVideoSource::MediaEngineDefaultVideoSource()
-  : mTimer(nullptr), mState(kReleased)
-{}
+  : mTimer(nullptr)
+{
+  mState = kReleased;
+}
 
 MediaEngineDefaultVideoSource::~MediaEngineDefaultVideoSource()
 {}
 
 void
 MediaEngineDefaultVideoSource::GetName(nsAString& aName)
 {
   aName.Assign(NS_LITERAL_STRING("Default Video Device"));
@@ -196,20 +198,45 @@ MediaEngineDefaultVideoSource::Notify(ns
 
   nsRefPtr<layers::PlanarYCbCrImage> image = mImage;
   segment.AppendFrame(image.forget(), USECS_PER_S / DEFAULT_FPS, gfxIntSize(DEFAULT_WIDTH, DEFAULT_HEIGHT));
   mSource->AppendToTrack(mTrackID, &segment);
 
   return NS_OK;
 }
 
-NS_IMPL_THREADSAFE_ISUPPORTS1(MediaEngineDefaultAudioSource, nsITimerCallback)
+void
+MediaEngineDefaultVideoSource::NotifyPull(MediaStreamGraph* aGraph,
+                                          StreamTime aDesiredTime)
+{
+  // Ignore - we push video data
+}
+
+
 /**
  * Default audio source.
  */
+NS_IMPL_THREADSAFE_ISUPPORTS1(MediaEngineDefaultAudioSource, nsITimerCallback)
+
+MediaEngineDefaultAudioSource::MediaEngineDefaultAudioSource()
+  : mTimer(nullptr)
+{
+  mState = kReleased;
+}
+
+MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource()
+{}
+
+void
+MediaEngineDefaultAudioSource::NotifyPull(MediaStreamGraph* aGraph,
+                                          StreamTime aDesiredTime)
+{
+  // Ignore - we push audio data
+}
+
 void
 MediaEngineDefaultAudioSource::GetName(nsAString& aName)
 {
   aName.Assign(NS_LITERAL_STRING("Default Audio Device"));
   return;
 }
 
 void
@@ -307,19 +334,49 @@ MediaEngineDefaultAudioSource::Notify(ns
 
   mSource->AppendToTrack(mTrackID, &segment);
 
   return NS_OK;
 }
 
 void
 MediaEngineDefault::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources) {
-  aVSources->AppendElement(mVSource);
+  int32_t found = false;
+  int32_t len = mVSources.Length();
+  for (int32_t i = 0; i < len; i++) {
+    nsRefPtr<MediaEngineVideoSource> source = mVSources.ElementAt(i);
+    aVSources->AppendElement(source);
+    if (source->IsAvailable()) {
+      found = true;
+    }
+  }
+
+  // All streams are currently busy, just make a new one.
+  if (!found) {
+    nsRefPtr<MediaEngineVideoSource> newSource =
+      new MediaEngineDefaultVideoSource();
+    mVSources.AppendElement(newSource);
+    aVSources->AppendElement(newSource);
+  }
   return;
 }
 
 void
 MediaEngineDefault::EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources) {
-  aASources->AppendElement(mASource);
+  int32_t len = mVSources.Length();
+  for (int32_t i = 0; i < len; i++) {
+    nsRefPtr<MediaEngineAudioSource> source = mASources.ElementAt(i);
+    if (source->IsAvailable()) {
+      aASources->AppendElement(source);
+    }
+  }
+
+  // All streams are currently busy, just make a new one.
+  if (aASources->Length() == 0) {
+    nsRefPtr<MediaEngineAudioSource> newSource =
+      new MediaEngineDefaultAudioSource();
+    mASources.AppendElement(newSource);
+    aASources->AppendElement(newSource);
+  }
   return;
 }
 
 } // namespace mozilla
--- a/content/media/webrtc/MediaEngineDefault.h
+++ b/content/media/webrtc/MediaEngineDefault.h
@@ -36,81 +36,76 @@ public:
   MediaEngineDefaultVideoSource();
   ~MediaEngineDefaultVideoSource();
 
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
 
   virtual const MediaEngineVideoOptions *GetOptions();
   virtual nsresult Allocate();
-
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop();
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
+  virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
 
   // Need something better...
   static const int DEFAULT_WIDTH=640;
   static const int DEFAULT_HEIGHT=480;
   static const int DEFAULT_FPS=30;
 
 protected:
   TrackID mTrackID;
   nsCOMPtr<nsITimer> mTimer;
   nsRefPtr<layers::ImageContainer> mImageContainer;
 
-  MediaEngineState mState;
   SourceMediaStream* mSource;
   layers::PlanarYCbCrImage* mImage;
   static const MediaEngineVideoOptions mOpts;
 };
 
 class MediaEngineDefaultAudioSource : public nsITimerCallback,
                                       public MediaEngineAudioSource
 {
 public:
-  MediaEngineDefaultAudioSource() : mTimer(nullptr), mState(kReleased) {}
-  ~MediaEngineDefaultAudioSource(){};
+  MediaEngineDefaultAudioSource();
+  ~MediaEngineDefaultAudioSource();
 
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
 
   virtual nsresult Allocate();
-
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop();
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
+  virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
 
 protected:
   TrackID mTrackID;
   nsCOMPtr<nsITimer> mTimer;
 
-  MediaEngineState mState;
   SourceMediaStream* mSource;
 };
 
 class MediaEngineDefault : public MediaEngine
 {
 public:
-  MediaEngineDefault() {
-    mVSource = new MediaEngineDefaultVideoSource();
-    mASource = new MediaEngineDefaultAudioSource();
-  }
+  MediaEngineDefault() {}
   ~MediaEngineDefault() {}
 
   virtual void EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
   virtual void EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
 
 private:
-  nsRefPtr<MediaEngineVideoSource> mVSource;
-  nsRefPtr<MediaEngineAudioSource> mASource;
+  nsTArray<nsRefPtr<MediaEngineVideoSource> > mVSources;
+  nsTArray<nsRefPtr<MediaEngineAudioSource> > mASources;
 };
 
 }
 
 #endif /* NSMEDIAENGINEDEFAULT_H_ */
--- a/content/media/webrtc/MediaEngineWebRTC.cpp
+++ b/content/media/webrtc/MediaEngineWebRTC.cpp
@@ -48,56 +48,73 @@ MediaEngineWebRTC::EnumerateVideoDevices
     mVideoEngineInit = true;
   }
 
   ptrViECapture = webrtc::ViECapture::GetInterface(mVideoEngine);
   if (!ptrViECapture) {
     return;
   }
 
+  /**
+   * We still enumerate every time, in case a new device was plugged in since
+   * the last call. TODO: Verify that WebRTC actually does deal with hotplugging
+   * new devices (with or without new engine creation) and accordingly adjust.
+   * Enumeration is not neccessary if GIPS reports the same set of devices
+   * for a given instance of the engine. Likewise, if a device was plugged out,
+   * mVideoSources must be updated.
+   */
   int num = ptrViECapture->NumberOfCaptureDevices();
   if (num <= 0) {
     return;
   }
 
   for (int i = 0; i < num; i++) {
-#ifdef DEBUG
     const unsigned int kMaxDeviceNameLength = 128; // XXX FIX!
     const unsigned int kMaxUniqueIdLength = 256;
     char deviceName[kMaxDeviceNameLength];
     char uniqueId[kMaxUniqueIdLength];
 
     // paranoia
     deviceName[0] = '\0';
     uniqueId[0] = '\0';
     int error = ptrViECapture->GetCaptureDevice(i, deviceName,
                                                 sizeof(deviceName), uniqueId,
                                                 sizeof(uniqueId));
+
+#ifdef DEBUG
     if (error) {
-      LOG((" VieCapture:GetCaptureDevice: Failed %d", 
+      LOG((" VieCapture:GetCaptureDevice: Failed %d",
            ptrViEBase->LastError() ));
       continue;
     }
     LOG(("  Capture Device Index %d, Name %s", i, deviceName));
 
     webrtc::CaptureCapability cap;
     int numCaps = ptrViECapture->NumberOfCapabilities(uniqueId, kMaxUniqueIdLength);
     LOG(("Number of Capabilities %d", numCaps));
     for (int j = 0; j < numCaps; j++) {
-      if (ptrViECapture->GetCaptureCapability(uniqueId, kMaxUniqueIdLength, 
+      if (ptrViECapture->GetCaptureCapability(uniqueId, kMaxUniqueIdLength,
                                               j, cap ) != 0 ) {
         break;
       }
       LOG(("type=%d width=%d height=%d maxFPS=%d",
            cap.rawType, cap.width, cap.height, cap.maxFPS ));
     }
 #endif
 
-    nsRefPtr<MediaEngineVideoSource> vSource = new MediaEngineWebRTCVideoSource(mVideoEngine, i);
-    aVSources->AppendElement(vSource.forget());
+    nsRefPtr<MediaEngineWebRTCVideoSource> vSource;
+    NS_ConvertUTF8toUTF16 uuid(uniqueId);
+    if (mVideoSources.Get(uuid, getter_AddRefs(vSource))) {
+      // We've already seen this device, just append.
+      aVSources->AppendElement(vSource.get());
+    } else {
+      vSource = new MediaEngineWebRTCVideoSource(mVideoEngine, i);
+      mVideoSources.Put(uuid, vSource); // Hashtable takes ownership.
+      aVSources->AppendElement(vSource);
+    }
   }
 
   ptrViEBase->Release();
   ptrViECapture->Release();
 
   return;
 }
 
@@ -131,41 +148,51 @@ MediaEngineWebRTC::EnumerateAudioDevices
     return;
   }
 
   int nDevices = 0;
   ptrVoEHw->GetNumOfRecordingDevices(nDevices);
   for (int i = 0; i < nDevices; i++) {
     // We use constants here because GetRecordingDeviceName takes char[128].
     char deviceName[128];
-    char uniqueID[128];
+    char uniqueId[128];
     // paranoia; jingle doesn't bother with this
     deviceName[0] = '\0';
-    uniqueID[0] = '\0';
+    uniqueId[0] = '\0';
+
+    ptrVoEHw->GetRecordingDeviceName(i, deviceName, uniqueId);
 
-    ptrVoEHw->GetRecordingDeviceName(i, deviceName, uniqueID);
-    nsRefPtr<MediaEngineAudioSource> aSource = new MediaEngineWebRTCAudioSource(
-      mVoiceEngine, i, deviceName, uniqueID
-    );
-    aASources->AppendElement(aSource.forget());
+    nsRefPtr<MediaEngineWebRTCAudioSource> aSource;
+    NS_ConvertUTF8toUTF16 uuid(uniqueId);
+    if (mAudioSources.Get(uuid, getter_AddRefs(aSource))) {
+      // We've already seen this device, just append.
+      aASources->AppendElement(aSource.get());
+    } else {
+      aSource = new MediaEngineWebRTCAudioSource(
+        mVoiceEngine, i, deviceName, uniqueId
+      );
+      mAudioSources.Put(uuid, aSource); // Hashtable takes ownership.
+      aASources->AppendElement(aSource);
+    }
   }
 
   ptrVoEHw->Release();
   ptrVoEBase->Release();
 }
 
-
 void
 MediaEngineWebRTC::Shutdown()
 {
   if (mVideoEngine) {
+    mVideoSources.Clear();
     webrtc::VideoEngine::Delete(mVideoEngine);
   }
 
   if (mVoiceEngine) {
+    mAudioSources.Clear();
     webrtc::VoiceEngine::Delete(mVoiceEngine);
   }
 
   mVideoEngine = NULL;
   mVoiceEngine = NULL;
 }
 
 }
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -48,35 +48,51 @@ namespace mozilla {
 /**
  * The WebRTC implementation of the MediaEngine interface.
  */
 class MediaEngineWebRTCVideoSource : public MediaEngineVideoSource,
                                      public webrtc::ExternalRenderer,
                                      public nsRunnable
 {
 public:
-  static const int DEFAULT_VIDEO_FPS = 30;
+  static const int DEFAULT_VIDEO_FPS = 60;
   static const int DEFAULT_MIN_VIDEO_FPS = 10;
 
   // ViEExternalRenderer.
   virtual int FrameSizeChange(unsigned int, unsigned int, unsigned int);
   virtual int DeliverFrame(unsigned char*, int, uint32_t, int64_t);
 
-  MediaEngineWebRTCVideoSource(webrtc::VideoEngine* videoEnginePtr,
-    int index, int aMinFps = DEFAULT_MIN_VIDEO_FPS);
-  ~MediaEngineWebRTCVideoSource();
+  MediaEngineWebRTCVideoSource(webrtc::VideoEngine* aVideoEnginePtr,
+    int aIndex, int aMinFps = DEFAULT_MIN_VIDEO_FPS)
+    : mVideoEngine(aVideoEnginePtr)
+    , mCaptureIndex(aIndex)
+    , mCapabilityChosen(false)
+    , mWidth(640)
+    , mHeight(480)
+    , mLastEndTime(0)
+    , mMonitor("WebRTCCamera.Monitor")
+    , mFps(DEFAULT_VIDEO_FPS)
+    , mMinFps(aMinFps)
+    , mInitDone(false)
+    , mInSnapshotMode(false)
+    , mSnapshotPath(NULL) {
+    mState = kReleased;
+    Init();
+  }
+  ~MediaEngineWebRTCVideoSource() { Shutdown(); }
 
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
   virtual const MediaEngineVideoOptions *GetOptions();
   virtual nsresult Allocate();
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop();
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
+  virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime);
 
   NS_DECL_ISUPPORTS
 
   // This runnable is for creating a temporary file on the main thread.
   NS_IMETHODIMP
   Run()
   {
     nsCOMPtr<nsIFile> tmp;
@@ -109,27 +125,28 @@ private:
   webrtc::ViECapture* mViECapture;
   webrtc::ViERender* mViERender;
   webrtc::CaptureCapability mCapability; // Doesn't work on OS X.
 
   int mCaptureIndex;
   bool mCapabilityChosen;
   int mWidth, mHeight;
   TrackID mTrackID;
+  TrackTicks mLastEndTime;
 
-  MediaEngineState mState;
   mozilla::ReentrantMonitor mMonitor; // Monitor for processing WebRTC frames.
   SourceMediaStream* mSource;
 
   int mFps; // Track rate (30 fps by default)
   int mMinFps; // Min rate we want to accept
   bool mInitDone;
   bool mInSnapshotMode;
   nsString* mSnapshotPath;
 
+  nsRefPtr<layers::Image> mImage;
   nsRefPtr<layers::ImageContainer> mImageContainer;
 
   PRLock* mSnapshotLock;
   PRCondVar* mSnapshotCondVar;
 
   // These are in UTF-8 but webrtc api uses char arrays
   char mDeviceName[KMaxDeviceNameLength];
   char mUniqueId[KMaxUniqueIdLength];
@@ -143,35 +160,33 @@ class MediaEngineWebRTCAudioSource : pub
 {
 public:
   MediaEngineWebRTCAudioSource(webrtc::VoiceEngine* voiceEngine, int aIndex,
     const char* name, const char* uuid)
     : mVoiceEngine(voiceEngine)
     , mMonitor("WebRTCMic.Monitor")
     , mCapIndex(aIndex)
     , mChannel(-1)
-    , mInitDone(false)
-    , mState(kReleased) {
-
-    mVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
+    , mInitDone(false) {
+    mState = kReleased;
     mDeviceName.Assign(NS_ConvertUTF8toUTF16(name));
     mDeviceUUID.Assign(NS_ConvertUTF8toUTF16(uuid));
-    mInitDone = true;
+    Init();
   }
-
   ~MediaEngineWebRTCAudioSource() { Shutdown(); }
 
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
 
   virtual nsresult Allocate();
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop();
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
+  virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime);
 
   // VoEMediaProcess.
   void Process(const int channel, const webrtc::ProcessingTypes type,
                WebRtc_Word16 audio10ms[], const int length,
                const int samplingFreq, const bool isStereo);
 
   NS_DECL_ISUPPORTS
 
@@ -187,46 +202,52 @@ private:
   webrtc::VoEExternalMedia* mVoERender;
 
   mozilla::ReentrantMonitor mMonitor;
 
   int mCapIndex;
   int mChannel;
   TrackID mTrackID;
   bool mInitDone;
-  MediaEngineState mState;
 
   nsString mDeviceName;
   nsString mDeviceUUID;
 
   SourceMediaStream* mSource;
 };
 
 class MediaEngineWebRTC : public MediaEngine
 {
 public:
   MediaEngineWebRTC()
   : mVideoEngine(NULL)
   , mVoiceEngine(NULL)
   , mVideoEngineInit(false)
-  , mAudioEngineInit(false) {}
-
+  , mAudioEngineInit(false) {
+    mVideoSources.Init();
+    mAudioSources.Init();
+  }
   ~MediaEngineWebRTC() { Shutdown(); }
 
   // Clients should ensure to clean-up sources video/audio sources
   // before invoking Shutdown on this class.
   void Shutdown();
 
   virtual void EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
   virtual void EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
 
 private:
   webrtc::VideoEngine* mVideoEngine;
   webrtc::VoiceEngine* mVoiceEngine;
 
   // Need this to avoid unneccesary WebRTC calls while enumerating.
   bool mVideoEngineInit;
   bool mAudioEngineInit;
+
+  // Store devices we've already seen in a hashtable for quick return.
+  // Maps UUID to MediaEngineSource (one set for audio, one for video).
+  nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCVideoSource > mVideoSources;
+  nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCAudioSource > mAudioSources;
 };
 
 }
 
 #endif /* NSMEDIAENGINEWEBRTC_H_ */
--- a/content/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -48,78 +48,32 @@ MediaEngineWebRTCAudioSource::GetUUID(ns
 
 nsresult
 MediaEngineWebRTCAudioSource::Allocate()
 {
   if (mState != kReleased) {
     return NS_ERROR_FAILURE;
   }
 
-  mVoEBase->Init();
-
-  mVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
-  if (!mVoERender) {
-    return NS_ERROR_FAILURE;
-  }
-
-  mChannel = mVoEBase->CreateChannel();
-  if (mChannel < 0) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // Check for availability.
-  webrtc::VoEHardware* ptrVoEHw = webrtc::VoEHardware::GetInterface(mVoiceEngine);
-  if (ptrVoEHw->SetRecordingDevice(mCapIndex)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  bool avail = false;
-  ptrVoEHw->GetRecordingDeviceStatus(avail);
-  if (!avail) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // Set "codec" to PCM, 32kHz on 1 channel
-  webrtc::VoECodec* ptrVoECodec;
-  webrtc::CodecInst codec;
-  ptrVoECodec = webrtc::VoECodec::GetInterface(mVoiceEngine);
-  if (!ptrVoECodec) {
-    return NS_ERROR_FAILURE;
-  }
-
-  strcpy(codec.plname, ENCODING);
-  codec.channels = CHANNELS;
-  codec.rate = SAMPLE_RATE;
-  codec.plfreq = SAMPLE_FREQUENCY;
-  codec.pacsize = SAMPLE_LENGTH;
-  codec.pltype = 0; // Default payload type
-
-  if (ptrVoECodec->SetSendCodec(mChannel, codec)) {
-    return NS_ERROR_FAILURE;
-  }
-
   // Audio doesn't play through unless we set a receiver and destination, so
   // we setup a dummy local destination, and do a loopback.
   mVoEBase->SetLocalReceiver(mChannel, DEFAULT_PORT);
   mVoEBase->SetSendDestination(mChannel, DEFAULT_PORT, "127.0.0.1");
 
   mState = kAllocated;
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCAudioSource::Deallocate()
 {
   if (mState != kStopped && mState != kAllocated) {
     return NS_ERROR_FAILURE;
   }
 
-  mVoEBase->Terminate();
-  mVoERender->Release();
-
   mState = kReleased;
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCAudioSource::Start(SourceMediaStream* aStream, TrackID aID)
 {
   if (!mInitDone || mState != kAllocated) {
@@ -130,16 +84,17 @@ MediaEngineWebRTCAudioSource::Start(Sour
   }
 
   mSource = aStream;
 
   AudioSegment* segment = new AudioSegment();
   segment->Init(CHANNELS);
   mSource->AddTrack(aID, SAMPLE_FREQUENCY, 0, segment);
   mSource->AdvanceKnownTracksTime(STREAM_TIME_MAX);
+  LOG(("Initial audio"));
   mTrackID = aID;
 
   if (mVoEBase->StartReceive(mChannel)) {
     return NS_ERROR_FAILURE;
   }
   if (mVoEBase->StartSend(mChannel)) {
     return NS_ERROR_FAILURE;
   }
@@ -165,56 +120,122 @@ MediaEngineWebRTCAudioSource::Stop()
 
   if (mVoEBase->StopSend(mChannel)) {
     return NS_ERROR_FAILURE;
   }
   if (mVoEBase->StopReceive(mChannel)) {
     return NS_ERROR_FAILURE;
   }
 
-  mState = kStopped;
+  {
+    ReentrantMonitorAutoEnter enter(mMonitor);
+    mState = kStopped;
+    mSource->EndTrack(mTrackID);
+  }
+
   return NS_OK;
 }
 
+void
+MediaEngineWebRTCAudioSource::NotifyPull(MediaStreamGraph* aGraph,
+                                         StreamTime aDesiredTime)
+{
+  // Ignore - we push audio data
+}
+
 nsresult
 MediaEngineWebRTCAudioSource::Snapshot(uint32_t aDuration, nsIDOMFile** aFile)
 {
    return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+void
+MediaEngineWebRTCAudioSource::Init()
+{
+  mVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
+
+  mVoEBase->Init();
+
+  mVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
+  if (!mVoERender) {
+    return;
+  }
+
+  mChannel = mVoEBase->CreateChannel();
+  if (mChannel < 0) {
+    return;
+  }
+
+  // Check for availability.
+  webrtc::VoEHardware* ptrVoEHw = webrtc::VoEHardware::GetInterface(mVoiceEngine);
+  if (ptrVoEHw->SetRecordingDevice(mCapIndex)) {
+    return;
+  }
+
+  bool avail = false;
+  ptrVoEHw->GetRecordingDeviceStatus(avail);
+  if (!avail) {
+    return;
+  }
+
+  // Set "codec" to PCM, 32kHz on 1 channel
+  webrtc::VoECodec* ptrVoECodec;
+  webrtc::CodecInst codec;
+  ptrVoECodec = webrtc::VoECodec::GetInterface(mVoiceEngine);
+  if (!ptrVoECodec) {
+    return;
+  }
+
+  strcpy(codec.plname, ENCODING);
+  codec.channels = CHANNELS;
+  codec.rate = SAMPLE_RATE;
+  codec.plfreq = SAMPLE_FREQUENCY;
+  codec.pacsize = SAMPLE_LENGTH;
+  codec.pltype = 0; // Default payload type
+
+  if (ptrVoECodec->SetSendCodec(mChannel, codec)) {
+    return;
+  }
+
+  mInitDone = true;
+}
 
 void
 MediaEngineWebRTCAudioSource::Shutdown()
 {
   if (!mInitDone) {
     return;
   }
 
   if (mState == kStarted) {
     Stop();
   }
 
   if (mState == kAllocated) {
     Deallocate();
   }
 
+  mVoEBase->Terminate();
+  mVoERender->Release();
   mVoEBase->Release();
 
   mState = kReleased;
   mInitDone = false;
 }
 
 typedef WebRtc_Word16 sample;
 
 void
 MediaEngineWebRTCAudioSource::Process(const int channel,
   const webrtc::ProcessingTypes type, sample* audio10ms,
   const int length, const int samplingFreq, const bool isStereo)
 {
   ReentrantMonitorAutoEnter enter(mMonitor);
+  if (mState != kStarted)
+    return;
 
   nsRefPtr<SharedBuffer> buffer = SharedBuffer::Create(length * sizeof(sample));
 
   sample* dest = static_cast<sample*>(buffer->Data());
   memcpy(dest, audio10ms, length * sizeof(sample));
 
   AudioSegment segment;
   segment.Init(CHANNELS);
--- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -16,56 +16,31 @@ extern PRLogModuleInfo* gMediaManagerLog
 #define LOG(msg)
 #endif
 
 /**
  * Webrtc video source.
  */
 NS_IMPL_THREADSAFE_ISUPPORTS1(MediaEngineWebRTCVideoSource, nsIRunnable)
 
-MediaEngineWebRTCVideoSource::MediaEngineWebRTCVideoSource(webrtc::VideoEngine* aVideoEnginePtr,
-                                                           int aIndex, int aMinFps)
-  : mVideoEngine(aVideoEnginePtr)
-  , mCaptureIndex(aIndex)
-  , mCapabilityChosen(false)
-  , mWidth(640)
-  , mHeight(480)
-  , mState(kReleased)
-  , mMonitor("WebRTCCamera.Monitor")
-  , mFps(DEFAULT_VIDEO_FPS)
-  , mMinFps(aMinFps)
-  , mInitDone(false)
-  , mInSnapshotMode(false)
-  , mSnapshotPath(NULL)
-{
-  Init();
-}
-
-MediaEngineWebRTCVideoSource::~MediaEngineWebRTCVideoSource()
-{
-  Shutdown();
-}
-
 // ViEExternalRenderer Callback.
 int
 MediaEngineWebRTCVideoSource::FrameSizeChange(
    unsigned int w, unsigned int h, unsigned int streams)
 {
   mWidth = w;
   mHeight = h;
   return 0;
 }
 
 // ViEExternalRenderer Callback. Process every incoming frame here.
 int
 MediaEngineWebRTCVideoSource::DeliverFrame(
    unsigned char* buffer, int size, uint32_t time_stamp, int64_t render_time)
 {
-  ReentrantMonitorAutoEnter enter(mMonitor);
-
   if (mInSnapshotMode) {
     // Set the condition variable to false and notify Snapshot().
     PR_Lock(mSnapshotLock);
     mInSnapshotMode = false;
     PR_NotifyCondVar(mSnapshotCondVar);
     PR_Unlock(mSnapshotLock);
     return 0;
   }
@@ -73,16 +48,17 @@ MediaEngineWebRTCVideoSource::DeliverFra
   // Check for proper state.
   if (mState != kStarted) {
     LOG(("DeliverFrame: video not started"));
     return 0;
   }
 
   // Create a video frame and append it to the track.
   ImageFormat format = PLANAR_YCBCR;
+
   nsRefPtr<layers::Image> image = mImageContainer->CreateImage(&format, 1);
 
   layers::PlanarYCbCrImage* videoImage = static_cast<layers::PlanarYCbCrImage*>(image.get());
 
   uint8_t* frame = static_cast<uint8_t*> (buffer);
   const uint8_t lumaBpp = 8;
   const uint8_t chromaBpp = 4;
 
@@ -96,20 +72,55 @@ MediaEngineWebRTCVideoSource::DeliverFra
   data.mCbCrSize = gfxIntSize(mWidth/ 2, mHeight/ 2);
   data.mPicX = 0;
   data.mPicY = 0;
   data.mPicSize = gfxIntSize(mWidth, mHeight);
   data.mStereoMode = STEREO_MODE_MONO;
 
   videoImage->SetData(data);
 
+#ifdef LOG_ALL_FRAMES
+  static uint32_t frame_num = 0;
+  LOG(("frame %d; timestamp %u, render_time %lu", frame_num++, time_stamp, render_time));
+#endif
+
+  // we don't touch anything in 'this' until here (except for snapshot,
+  // which has it's own lock)
+  ReentrantMonitorAutoEnter enter(mMonitor);
+
+  // implicitly releases last image
+  mImage = image.forget();
+
+  return 0;
+}
+
+// Called if the graph thinks it's running out of buffered video; repeat
+// the last frame for whatever minimum period it think it needs.  Note that
+// this means that no *real* frame can be inserted during this period.
+void
+MediaEngineWebRTCVideoSource::NotifyPull(MediaStreamGraph* aGraph,
+                                         StreamTime aDesiredTime)
+{
   VideoSegment segment;
-  segment.AppendFrame(image.forget(), 1, gfxIntSize(mWidth, mHeight));
+
+  ReentrantMonitorAutoEnter enter(mMonitor);
+  if (mState != kStarted)
+    return;
+
+  // Note: we're not giving up mImage here
+  nsRefPtr<layers::Image> image = mImage;
+  TrackTicks target = TimeToTicksRoundUp(USECS_PER_S, aDesiredTime);
+  TrackTicks delta = target - mLastEndTime;
+#ifdef LOG_ALL_FRAMES
+  LOG(("NotifyPull, target = %lu, delta = %lu", (uint64_t) target, (uint64_t) delta));
+#endif
+  // NULL images are allowed
+  segment.AppendFrame(image ? image.forget() : nullptr, delta, gfxIntSize(mWidth, mHeight));
   mSource->AppendToTrack(mTrackID, &(segment));
-  return 0;
+  mLastEndTime = target;
 }
 
 void
 MediaEngineWebRTCVideoSource::ChooseCapability(uint32_t aWidth, uint32_t aHeight, uint32_t aMinFPS)
 {
   int num = mViECapture->NumberOfCapabilities(mUniqueId, KMaxUniqueIdLength);
 
   NS_WARN_IF_FALSE(!mCapabilityChosen,"Shouldn't select capability of a device twice");
@@ -186,32 +197,27 @@ MediaEngineWebRTCVideoSource::Allocate()
     // XXX these should come from constraints
     ChooseCapability(mWidth, mHeight, mMinFps);
   }
 
   if (mViECapture->AllocateCaptureDevice(mUniqueId, KMaxUniqueIdLength, mCaptureIndex)) {
     return NS_ERROR_FAILURE;
   }
 
-  if (mViECapture->StartCapture(mCaptureIndex, mCapability) < 0) {
-    return NS_ERROR_FAILURE;
-  }
-
   mState = kAllocated;
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCVideoSource::Deallocate()
 {
   if (mState != kStopped && mState != kAllocated) {
     return NS_ERROR_FAILURE;
   }
 
-  mViECapture->StopCapture(mCaptureIndex);
   mViECapture->ReleaseCaptureDevice(mCaptureIndex);
   mState = kReleased;
   return NS_OK;
 }
 
 const MediaEngineVideoOptions*
 MediaEngineWebRTCVideoSource::GetOptions()
 {
@@ -236,47 +242,56 @@ MediaEngineWebRTCVideoSource::Start(Sour
   if (mState == kStarted) {
     return NS_OK;
   }
 
   mSource = aStream;
   mTrackID = aID;
 
   mImageContainer = layers::LayerManager::CreateImageContainer();
-  mSource->AddTrack(aID, mFps, 0, new VideoSegment());
+
+  mSource->AddTrack(aID, USECS_PER_S, 0, new VideoSegment());
   mSource->AdvanceKnownTracksTime(STREAM_TIME_MAX);
+  mLastEndTime = 0;
 
   error = mViERender->AddRenderer(mCaptureIndex, webrtc::kVideoI420, (webrtc::ExternalRenderer*)this);
   if (error == -1) {
     return NS_ERROR_FAILURE;
   }
 
   error = mViERender->StartRender(mCaptureIndex);
   if (error == -1) {
     return NS_ERROR_FAILURE;
   }
 
+  if (mViECapture->StartCapture(mCaptureIndex, mCapability) < 0) {
+    return NS_ERROR_FAILURE;
+  }
+
   mState = kStarted;
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCVideoSource::Stop()
 {
   if (mState != kStarted) {
     return NS_ERROR_FAILURE;
   }
 
-  mSource->EndTrack(mTrackID);
-  mSource->Finish();
+  {
+    ReentrantMonitorAutoEnter enter(mMonitor);
+    mState = kStopped;
+    mSource->EndTrack(mTrackID);
+  }
 
   mViERender->StopRender(mCaptureIndex);
   mViERender->RemoveRenderer(mCaptureIndex);
+  mViECapture->StopCapture(mCaptureIndex);
 
-  mState = kStopped;
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCVideoSource::Snapshot(uint32_t aDuration, nsIDOMFile** aFile)
 {
   /**
    * To get a Snapshot we do the following:
--- a/db/sqlite3/README.MOZILLA
+++ b/db/sqlite3/README.MOZILLA
@@ -1,11 +1,11 @@
-This is sqlite 3.7.14
+This is sqlite 3.7.14.1
 
--- Ryan VanderMeulen <ryanvm@gmail.com>, 09/2012
+-- Ryan VanderMeulen <ryanvm@gmail.com>, 10/2012
 
 See http://www.sqlite.org/ for more info.
 
 We have a mozilla-specific Makefile.in in src/ (normally no
 Makefile.in there) that we use to build.
 
 To move to a new version:
 
--- a/db/sqlite3/src/sqlite3.c
+++ b/db/sqlite3/src/sqlite3.c
@@ -1,11 +1,11 @@
 /******************************************************************************
 ** This file is an amalgamation of many separate C source files from SQLite
-** version 3.7.14.  By combining all the individual C code files into this 
+** version 3.7.14.1.  By combining all the individual C code files into this 
 ** single large file, the entire code can be compiled as a single translation
 ** unit.  This allows many compilers to do optimizations that would not be
 ** possible if the files were compiled separately.  Performance improvements
 ** of 5% or more are commonly seen when SQLite is compiled as a single
 ** translation unit.
 **
 ** This file is all you need to compile SQLite.  To use SQLite in other
 ** programs, you need this file and the "sqlite3.h" header file that defines
@@ -668,19 +668,19 @@ extern "C" {
 ** within its configuration management system.  ^The SQLITE_SOURCE_ID
 ** string contains the date and time of the check-in (UTC) and an SHA1
 ** hash of the entire source tree.
 **
 ** See also: [sqlite3_libversion()],
 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
 ** [sqlite_version()] and [sqlite_source_id()].
 */
-#define SQLITE_VERSION        "3.7.14"
+#define SQLITE_VERSION        "3.7.14.1"
 #define SQLITE_VERSION_NUMBER 3007014
-#define SQLITE_SOURCE_ID      "2012-09-03 15:42:36 c0d89d4a9752922f9e367362366efde4f1b06f2a"
+#define SQLITE_SOURCE_ID      "2012-10-04 19:37:12 091570e46d04e84b67228e0bdbcd6e1fb60c6bdb"
 
 /*
 ** CAPI3REF: Run-Time Library Version Numbers
 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
 **
 ** These interfaces provide the same information as the [SQLITE_VERSION],
 ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
 ** but are associated with the library instead of the header file.  ^(Cautious
@@ -53844,16 +53844,19 @@ static void copyNodeContent(MemPage *pFr
 ** a maximum of four divider cells into the parent page, and the maximum
 ** size of a cell stored within an internal node is always less than 1/4
 ** of the page-size, the aOvflSpace[] buffer is guaranteed to be large
 ** enough for all overflow cells.
 **
 ** If aOvflSpace is set to a null pointer, this function returns 
 ** SQLITE_NOMEM.
 */
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
+#pragma optimize("", off)
+#endif
 static int balance_nonroot(
   MemPage *pParent,               /* Parent page of siblings being balanced */
   int iParentIdx,                 /* Index of "the page" in pParent */
   u8 *aOvflSpace,                 /* page-size bytes of space for parent ovfl */
   int isRoot,                     /* True if pParent is a root-page */
   int bBulk                       /* True if this call is part of a bulk load */
 ){
   BtShared *pBt;               /* The whole database */
@@ -54474,16 +54477,19 @@ balance_cleanup:
     releasePage(apOld[i]);
   }
   for(i=0; i<nNew; i++){
     releasePage(apNew[i]);
   }
 
   return rc;
 }
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
+#pragma optimize("", on)
+#endif
 
 
 /*
 ** This function is called when the root page of a b-tree structure is
 ** overfull (has one or more overflow pages).
 **
 ** A new child page is allocated and the contents of the current root
 ** page, including overflow cells, are copied into the child. The root
@@ -106081,17 +106087,17 @@ static Bitmask codeOneLoopStart(
           }
 
           /* Finish the loop through table entries that match term pOrTerm. */
           sqlite3WhereEnd(pSubWInfo);
         }
       }
     }
     pLevel->u.pCovidx = pCov;
-    pLevel->iIdxCur = iCovCur;
+    if( pCov ) pLevel->iIdxCur = iCovCur;
     if( pAndExpr ){
       pAndExpr->pLeft = 0;
       sqlite3ExprDelete(pParse->db, pAndExpr);
     }
     sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
     sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk);
     sqlite3VdbeResolveLabel(v, iLoopBody);
 
--- a/db/sqlite3/src/sqlite3.h
+++ b/db/sqlite3/src/sqlite3.h
@@ -102,19 +102,19 @@ extern "C" {
 ** within its configuration management system.  ^The SQLITE_SOURCE_ID
 ** string contains the date and time of the check-in (UTC) and an SHA1
 ** hash of the entire source tree.
 **
 ** See also: [sqlite3_libversion()],
 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
 ** [sqlite_version()] and [sqlite_source_id()].
 */
-#define SQLITE_VERSION        "3.7.14"
+#define SQLITE_VERSION        "3.7.14.1"
 #define SQLITE_VERSION_NUMBER 3007014
-#define SQLITE_SOURCE_ID      "2012-09-03 15:42:36 c0d89d4a9752922f9e367362366efde4f1b06f2a"
+#define SQLITE_SOURCE_ID      "2012-10-04 19:37:12 091570e46d04e84b67228e0bdbcd6e1fb60c6bdb"
 
 /*
 ** CAPI3REF: Run-Time Library Version Numbers
 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
 **
 ** These interfaces provide the same information as the [SQLITE_VERSION],
 ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
 ** but are associated with the library instead of the header file.  ^(Cautious
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4929,16 +4929,33 @@ nsDocShell::Destroy()
 
     // Cancel any timers that were set for this docshell; this is needed
     // to break the cycle between us and the timers.
     CancelRefreshURITimers();
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
+{
+    if (mParentWidget) {
+        *aScale = mParentWidget->GetDefaultScale();
+        return NS_OK;
+    }
+
+    nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
+    if (ownerWindow) {
+        return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
+    }
+
+    *aScale = 1.0;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::SetPosition(int32_t x, int32_t y)
 {
     mBounds.x = x;
     mBounds.y = y;
 
     if (mContentViewer)
         NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE);
 
@@ -4985,16 +5002,23 @@ nsDocShell::SetPositionAndSize(int32_t x
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetPositionAndSize(int32_t * x, int32_t * y, int32_t * cx,
                                int32_t * cy)
 {
+    if (mParentWidget) {
+        // ensure size is up-to-date if window has changed resolution
+        nsIntRect r;
+        mParentWidget->GetClientBounds(r);
+        SetPositionAndSize(mBounds.x, mBounds.y, r.width, r.height, false);
+    }
+
     // We should really consider just getting this information from
     // our window instead of duplicating the storage and code...
     if (cx || cy) {
         // Caller wants to know our size; make sure to give them up to
         // date information.
         nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(mParent)));
         if (doc) {
             doc->FlushPendingNotifications(Flush_Layout);
--- a/docshell/shistory/src/nsSHistory.cpp
+++ b/docshell/shistory/src/nsSHistory.cpp
@@ -55,17 +55,17 @@ static PRCList gSHistoryList;
 // Max viewers allowed total, across all SHistory objects - negative default
 // means we will calculate how many viewers to cache based on total memory
 int32_t nsSHistory::sHistoryMaxTotalViewers = -1;
 
 // A counter that is used to be able to know the order in which
 // entries were touched, so that we can evict older entries first.
 static uint32_t gTouchCounter = 0;
 
-static PRLogModuleInfo* gLogModule = PR_LOG_DEFINE("nsSHistory");
+static PRLogModuleInfo* gLogModule = PR_NewLogModule("nsSHistory");
 #define LOG(format) PR_LOG(gLogModule, PR_LOG_DEBUG, format)
 
 // This macro makes it easier to print a log message which includes a URI's
 // spec.  Example use:
 //
 //  nsIURI *uri = [...];
 //  LOG_SPEC(("The URI is %s.", _spec), uri);
 //
--- a/dom/activities/src/ActivityProxy.js
+++ b/dom/activities/src/ActivityProxy.js
@@ -6,17 +6,16 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/ObjectWrapper.jsm");
-Cu.import("resource://gre/modules/Webapps.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsISyncMessageSender");
 
 function debug(aMsg) {
   //dump("-- ActivityProxy " + Date.now() + " : " + aMsg + "\n");
 }
@@ -25,16 +24,21 @@ function debug(aMsg) {
   * nsIActivityProxy implementation
   * We keep a reference to the C++ Activity object, and
   * communicate with the Message Manager to know when to
   * fire events on it.
   */
 function ActivityProxy() {
   debug("ActivityProxy");
   this.activity = null;
+  let inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
+                   .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+  debug("inParent: " + inParent);
+  Cu.import(inParent ? "resource://gre/modules/Webapps.jsm"
+                     : "resource://gre/modules/AppsServiceChild.jsm");
 }
 
 ActivityProxy.prototype = {
   startActivity: function actProxy_startActivity(aActivity, aOptions, aWindow) {
     debug("startActivity");
 
     this.window = aWindow;
     this.activity = aActivity;
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -119,34 +119,39 @@ let DOMApplicationRegistry = {
         }
         aNext();
       }).bind(this));
     } else {
       aNext();
     }
   },
 
-  // We are done with loading and initializing. Notify and
-  // save a copy of the registry.
-  onInitDone: function onInitDone() {
+  // Notify we are starting with registering apps.
+  notifyAppsRegistryStart: function notifyAppsRegistryStart() {
+    Services.obs.notifyObservers(this, "webapps-registry-start", null);
+  },
+
+  // Notify we are done with registering apps and save a copy of the registry.
+  notifyAppsRegistryReady: function notifyAppsRegistryReady() {
     Services.obs.notifyObservers(this, "webapps-registry-ready", null);
     this._saveApps();
   },
 
-  // registers all the activities and system messages
+  // Registers all the activities and system messages.
   registerAppsHandlers: function registerAppsHandlers() {
+    this.notifyAppsRegistryStart();
 #ifdef MOZ_SYS_MSG
     let ids = [];
     for (let id in this.webapps) {
       ids.push({ id: id });
     }
     this._processManifestForIds(ids);
 #else
     // Nothing else to do but notifying we're ready.
-    this.onInitDone();
+    this.notifyAppsRegistryReady();
 #endif
   },
 
   updatePermissionsForApp: function updatePermissionsForApp(aId) {
     // Install the permissions for this app, as if we were updating
     // to cleanup the old ones if needed.
     this._readManifests([{ id: aId }], (function(aResult) {
       let data = aResult[0];
@@ -243,17 +248,16 @@ let DOMApplicationRegistry = {
       }
     }
     this.registerAppsHandlers();
 #endif
     }).bind(this));
   },
 
 #ifdef MOZ_SYS_MSG
-
   // aEntryPoint is either the entry_point name or the null, in which case we
   // use the root of the manifest.
   _registerSystemMessagesForEntryPoint: function(aManifest, aApp, aEntryPoint) {
     let root = aManifest;
     if (aEntryPoint && aManifest.entry_points[aEntryPoint]) {
       root = aManifest.entry_points[aEntryPoint];
     }
 
@@ -365,20 +369,24 @@ let DOMApplicationRegistry = {
       return;
     }
 
     for (let entryPoint in aManifest.entry_points) {
       this._unregisterActivitiesForEntryPoint(aManifest, aApp, entryPoint);
     }
   },
 
-  _processManifestForIds: function(aIds) {
+  _initRegisterActivities: function() {
     this.activitiesToRegister = 0;
     this.activitiesRegistered = 0;
     this.allActivitiesSent = false;
+  },
+
+  _processManifestForIds: function(aIds) {
+    this._initRegisterActivities();
     this._readManifests(aIds, (function registerManifests(aResults) {
       aResults.forEach(function registerManifest(aResult) {
         let app = this.webapps[aResult.id];
         let manifest = aResult.manifest;
         app.name = manifest.name;
         this._registerSystemMessages(manifest, app);
         this._registerActivities(manifest, app);
       }, this);
@@ -535,17 +543,17 @@ let DOMApplicationRegistry = {
         break;
       case "Webapps::ApplyDownload":
         this.ApplyDownload(msg.manifestURL);
         break;
       case "Activities:Register:OK":
         this.activitiesRegistered++;
         if (this.allActivitiesSent &&
             this.activitiesRegistered === this.activitiesToRegister) {
-          this.onInitDone();
+          this.notifyAppsRegistryReady();
         }
         break;
       case "child-process-shutdown":
         this.removeMessageListener(mm);
         break;
     }
   },
 
@@ -770,23 +778,29 @@ let DOMApplicationRegistry = {
         aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
       });
     }
 
     function updateHostedApp(aManifest) {
       debug("updateHostedApp");
       let id = this._appId(app.origin);
 
+      // Update the web apps' registration.
+      this.notifyAppsRegistryStart();
 #ifdef MOZ_SYS_MSG
-      // Update the Web Activities
+      this._initRegisterActivities();
       this._readManifests([{ id: id }], (function unregisterManifest(aResult) {
         this._unregisterActivities(aResult[0].manifest, app);
         this._registerSystemMessages(aManifest, app);
         this._registerActivities(aManifest, app);
+        this.allActivitiesSent = true;
       }).bind(this));
+#else
+      // Nothing else to do but notifying we're ready.
+      this.notifyAppsRegistryReady();
 #endif
 
       // Store the new manifest.
       let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
       let manFile = dir.clone();
       manFile.append("manifest.webapp");
       this._writeFile(manFile, JSON.stringify(aManifest), function() { });
 
@@ -977,22 +991,28 @@ let DOMApplicationRegistry = {
 
     if (!aFromSync)
       this._saveApps((function() {
         this.broadcastMessage("Webapps:Install:Return:OK", aData);
         Services.obs.notifyObservers(this, "webapps-sync-install", appNote);
         this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject });
       }).bind(this));
 
+    if (!aData.isPackage) {
+      this.notifyAppsRegistryStart();
 #ifdef MOZ_SYS_MSG
-    if (!aData.isPackage) {
+      this._initRegisterActivities();
       this._registerSystemMessages(app.manifest, app);
       this._registerActivities(app.manifest, app);
+      this.allActivitiesSent = true;
+#else
+      // Nothing else to do but notifying we're ready.
+      this.notifyAppsRegistryReady();
+#endif
     }
-#endif
 
     this.startOfflineCacheDownload(manifest, appObject, aProfileDir, aOfflineCacheObserver);
     if (manifest.package_path) {
       // origin for install apps is meaningless here, since it's app:// and this
       // can't be used to resolve package paths.
       manifest = new ManifestHelper(jsonManifest, app.manifestURL);
       this.downloadPackage(manifest, appObject, false, function(aId, aManifest) {
         // Success! Move the zip out of TmpD.
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -8477,23 +8477,23 @@ nsDOMStringMapSH::Enumerate(nsIXPConnect
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMStringMapSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
                             JSObject *globalObj, JSObject **parentObj)
 {
-  *parentObj = globalObj;
-
-  nsDOMStringMap* dataset = static_cast<nsDOMStringMap*>(nativeObj);
-
-  // Parent the string map to its element.
-  nsINode* element = dataset->GetElement();
-  return WrapNativeParent(cx, globalObj, element, element, parentObj);
+  nsDOMStringMap* map = nsDOMStringMap::FromSupports(nativeObj);
+  nsINode* native_parent = map->GetParentObject();
+  if (!native_parent) {
+    return nsDOMClassInfo::PreCreate(nativeObj, cx, globalObj, parentObj);
+  }
+
+  return WrapNativeParent(cx, globalObj, native_parent, parentObj);
 }
 
 NS_IMETHODIMP
 nsDOMStringMapSH::DelProperty(nsIXPConnectWrappedNative *wrapper,
                               JSContext *cx, JSObject *obj, jsid id,
                               jsval *vp, bool *_retval)
 {
   nsCOMPtr<nsIDOMDOMStringMap> dataset(do_QueryWrappedNative(wrapper, obj));
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1593,16 +1593,18 @@ nsDOMWindowUtils::SendCompositionEvent(c
   }
 
   nsCompositionEvent compositionEvent(true, msg, widget);
   InitEvent(compositionEvent);
   if (msg != NS_COMPOSITION_START) {
     compositionEvent.data = aData;
   }
 
+  compositionEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+
   nsEventStatus status;
   nsresult rv = widget->DispatchEvent(&compositionEvent, status);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 static void
@@ -1670,16 +1672,18 @@ nsDOMWindowUtils::SendTextEvent(const ns
     textRanges.AppendElement(range);
   }
 
   textEvent.theText = aCompositionString;
 
   textEvent.rangeCount = textRanges.Length();
   textEvent.rangeArray = textRanges.Elements();
 
+  textEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+
   nsEventStatus status;
   nsresult rv = widget->DispatchEvent(&textEvent, status);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -807,23 +807,16 @@ nsFocusManager::ContentRemoved(nsIDocume
 
   // if the content is currently focused in the window, or is an ancestor
   // of the currently focused element, reset the focus within that window.
   nsIContent* content = window->GetFocusedNode();
   if (content && nsContentUtils::ContentIsDescendantOf(content, aContent)) {
     bool shouldShowFocusRing = window->ShouldShowFocusRing();
     window->SetFocusedNode(nullptr);
 
-    nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
-    if (docShell) {
-      nsCOMPtr<nsIPresShell> presShell;
-      docShell->GetPresShell(getter_AddRefs(presShell));
-      nsIMEStateManager::OnRemoveContent(presShell->GetPresContext(), content);
-    }
-
     // if this window is currently focused, clear the global focused
     // element as well, but don't fire any events.
     if (window == mFocusedWindow) {
       mFocusedContent = nullptr;
     }
     else {
       // Check if the node that was focused is an iframe or similar by looking
       // if it has a subdocument. This would indicate that this focused iframe
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1264,17 +1264,17 @@ MarkXBLHandlers(nsXBLPrototypeHandler* a
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow)
   if (tmp->IsBlackForCC()) {
     if (tmp->mCachedXBLPrototypeHandlers.IsInitialized()) {
       tmp->mCachedXBLPrototypeHandlers.EnumerateRead(MarkXBLHandlers, nullptr);
     }
     nsEventListenerManager* elm = tmp->GetListenerManager(false);
     if (elm) {
-      elm->UnmarkGrayJSListeners();
+      elm->MarkForCC();
     }
     tmp->UnmarkGrayTimers();
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindow)
   return tmp->IsBlackForCC();
@@ -8262,17 +8262,35 @@ nsGlobalWindow::UpdateCanvasFocus(bool a
   }
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetComputedStyle(nsIDOMElement* aElt,
                                  const nsAString& aPseudoElt,
                                  nsIDOMCSSStyleDeclaration** aReturn)
 {
-  FORWARD_TO_OUTER(GetComputedStyle, (aElt, aPseudoElt, aReturn),
+  return GetComputedStyleHelper(aElt, aPseudoElt, false, aReturn);
+}
+
+NS_IMETHODIMP
+nsGlobalWindow::GetDefaultComputedStyle(nsIDOMElement* aElt,
+                                        const nsAString& aPseudoElt,
+                                        nsIDOMCSSStyleDeclaration** aReturn)
+{
+  return GetComputedStyleHelper(aElt, aPseudoElt, true, aReturn);
+}
+
+nsresult
+nsGlobalWindow::GetComputedStyleHelper(nsIDOMElement* aElt,
+                                       const nsAString& aPseudoElt,
+                                       bool aDefaultStylesOnly,
+                                       nsIDOMCSSStyleDeclaration** aReturn)
+{
+  FORWARD_TO_OUTER(GetComputedStyleHelper, (aElt, aPseudoElt,
+                                            aDefaultStylesOnly, aReturn),
                    NS_ERROR_NOT_INITIALIZED);
 
   NS_ENSURE_ARG_POINTER(aReturn);
   *aReturn = nullptr;
 
   if (!aElt) {
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   }
@@ -8280,23 +8298,44 @@ nsGlobalWindow::GetComputedStyle(nsIDOME
   if (!mDocShell) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIPresShell> presShell;
   mDocShell->GetPresShell(getter_AddRefs(presShell));
 
   if (!presShell) {
-    return NS_OK;
+    // Try flushing frames on our parent in case there's a pending
+    // style change that will create the presshell.
+    nsGlobalWindow *parent =
+      static_cast<nsGlobalWindow *>(GetPrivateParent());
+    if (!parent) {
+      return NS_OK;
+    }
+
+    parent->FlushPendingNotifications(Flush_Frames);
+
+    // Might have killed mDocShell
+    if (!mDocShell) {
+      return NS_OK;
+    }
+
+    mDocShell->GetPresShell(getter_AddRefs(presShell));
+
+    if (!presShell) {
+      return NS_OK;
+    }
   }
 
   nsCOMPtr<dom::Element> element = do_QueryInterface(aElt);
   NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
   nsRefPtr<nsComputedDOMStyle> compStyle =
-    NS_NewComputedDOMStyle(element, aPseudoElt, presShell);
+    NS_NewComputedDOMStyle(element, aPseudoElt, presShell,
+                           aDefaultStylesOnly ? nsComputedDOMStyle::eDefaultOnly :
+                                                nsComputedDOMStyle::eAll);
 
   *aReturn = compStyle.forget().get();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -927,16 +927,22 @@ protected:
   nsresult GetTopImpl(nsIDOMWindow **aWindow, bool aScriptable);
 
   // Helper for creating performance objects.
   void CreatePerformanceObjectIfNeeded();
 
   // Outer windows only.
   nsDOMWindowList* GetWindowList();
 
+  // Helper for getComputedStyle and getDefaultComputedStyle
+  nsresult GetComputedStyleHelper(nsIDOMElement* aElt,
+                                  const nsAString& aPseudoElt,
+                                  bool aDefaultStylesOnly,
+                                  nsIDOMCSSStyleDeclaration** aReturn);
+
   // When adding new member variables, be careful not to create cycles
   // through JavaScript.  If there is any chance that a member variable
   // could own objects that are implemented in JavaScript, then those
   // objects will keep the global object (this object) alive.  To prevent
   // these cycles, ownership of such members must be released in
   // |CleanUp| and |DetachFromDocShell|.
 
   // This member is also used on both inner and outer windows, but
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -207,20 +207,23 @@ nsLocation::CheckURL(nsIURI* aURI, nsIDo
     // Make the load's referrer reflect changes to the document's URI caused by
     // push/replaceState, if possible.  First, get the document corresponding to
     // fp.  If the document's original URI (i.e. its URI before
     // push/replaceState) matches the principal's URI, use the document's
     // current URI as the referrer.  If they don't match, use the principal's
     // URI.
 
     JSScript* script = nullptr;
-    if (!JS_DescribeScriptedCaller(cx, &script, nullptr))
-      return NS_ERROR_FAILURE;
-    nsCOMPtr<nsIDocument> doc = GetScriptDocument(cx, script);
+    nsCOMPtr<nsIDocument> doc;
     nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI;
+    // NB: A false return value from JS_DescribeScriptedCaller means no caller
+    // was found. It does not signal that an exception was thrown.
+    if (JS_DescribeScriptedCaller(cx, &script, nullptr)) {
+      doc = GetScriptDocument(cx, script);
+    }
     if (doc) {
       docOriginalURI = doc->GetOriginalURI();
       docCurrentURI = doc->GetDocumentURI();
       rv = doc->NodePrincipal()->GetURI(getter_AddRefs(principalURI));
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     bool urisEqual = false;
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -232,17 +232,19 @@ CollectWindowReports(nsGlobalWindow *aWi
   REPORT("/layout/pres-contexts", windowSizes.mLayoutPresContext,
          "Memory used for the PresContext in the PresShell's frame "
          "within a window.");
   aWindowTotalSizes->mLayoutPresContext += windowSizes.mLayoutPresContext;
 
   // There are many different kinds of frames, but it is very likely
   // that only a few matter.  Implement a cutoff so we don't bloat
   // about:memory with many uninteresting entries.
-  static const size_t FRAME_SUNDRIES_THRESHOLD = 8192;
+  const size_t FRAME_SUNDRIES_THRESHOLD =
+    js::MemoryReportingSundriesThreshold();
+
   size_t frameSundriesSize = 0;
 #define FRAME_ID(classname)                                             \
   {                                                                     \
     size_t frameSize                                                    \
       = windowSizes.mArenaStats.FRAME_ID_STAT_FIELD(classname);         \
     if (frameSize < FRAME_SUNDRIES_THRESHOLD) {                         \
       frameSundriesSize += frameSize;                                   \
     } else {                                                            \
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -381,16 +381,21 @@ DOMInterfaces = {
    'headerFile': 'WebGLExtensions.h'
 },
 
 'WebGLExtensionDepthTexture': {
    'nativeType': 'mozilla::WebGLExtensionDepthTexture',
    'headerFile': 'WebGLExtensions.h'
 },
 
+'WebGLExtensionDebugRendererInfo': {
+   'nativeType': 'mozilla::WebGLExtensionDebugRendererInfo',
+   'headerFile': 'WebGLExtensions.h'
+},
+
 'WebGLExtensionLoseContext': {
    'nativeType': 'mozilla::WebGLExtensionLoseContext',
    'headerFile': 'WebGLExtensions.h'
 },
 
 'WebGLExtensionStandardDerivatives': {
    'nativeType': 'mozilla::WebGLExtensionStandardDerivatives',
    'headerFile': 'WebGLExtensions.h'
@@ -421,17 +426,17 @@ DOMInterfaces = {
    'headerFile': 'WebGLContext.h'
 },
 
 'WebGLRenderingContext': {
   'nativeType': 'mozilla::WebGLContext',
   'headerFile': 'WebGLContext.h',
   'resultNotAddRefed': [ 'canvas', 'getContextAttributes', 'getExtension',
                          'getAttachedShaders' ],
-  'implicitJSContext': [ 'texImage2D', 'texSubImage2D' ],
+  'implicitJSContext': [ 'texImage2D', 'texSubImage2D', 'getSupportedExtensions' ],
 },
 
 'WebGLShader': {
    'nativeType': 'mozilla::WebGLShader',
    'headerFile': 'WebGLContext.h'
 },
 
 'WebGLShaderPrecisionFormat': {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1706,16 +1706,17 @@ def typeIsSequenceOrHasSequenceMember(ty
         return typeIsSequenceOrHasSequenceMember(elementType)
     if type.isDictionary():
         return dictionaryHasSequenceMember(type.inner)
     if type.isUnion():
         return any(typeIsSequenceOrHasSequenceMember(m.type) for m in
                    type.flatMemberTypes)
     return False
 
+# If this function is modified, modify CGExampleMember.getArg accordingly
 def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
                                     isDefinitelyObject=False,
                                     isMember=False,
                                     isOptional=False,
                                     invalidEnumValueFatal=True,
                                     defaultValue=None,
                                     treatNullAs="Default",
                                     treatUndefinedAs="Default",
@@ -2534,27 +2535,30 @@ for (uint32_t i = 0; i < length; ++i) {
 
     conversionBehavior = "eDefault"
     if isEnforceRange:
         conversionBehavior = "eEnforceRange"
     elif isClamp:
         conversionBehavior = "eClamp"
 
     if type.nullable():
-        dataLoc = "${declName}.SetValue()"
+        declType = CGGeneric("Nullable<" + typeName + ">")
+        mutableType = declType.define() + "&"
+        if not isOptional and not isMember:
+            declType = CGWrapper(declType, pre="const ")
+        dataLoc = ("const_cast< %s >(${declName}).SetValue()" % mutableType)
         nullCondition = "${val}.isNullOrUndefined()"
         if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
             nullCondition = "!(${haveValue}) || " + nullCondition
         template = (
             "if (%s) {\n"
-            "  ${declName}.SetNull();\n"
+            "  const_cast< %s >(${declName}).SetNull();\n"
             "} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
             "  return false;\n"
-            "}" % (nullCondition, typeName, conversionBehavior, dataLoc))
-        declType = CGGeneric("Nullable<" + typeName + ">")
+            "}" % (nullCondition, mutableType, typeName, conversionBehavior, dataLoc))
     else:
         assert(defaultValue is None or
                not isinstance(defaultValue, IDLNullValue))
         dataLoc = "${declName}"
         template = (
             "if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
             "  return false;\n"
             "}" % (typeName, conversionBehavior, dataLoc))
@@ -3005,16 +3009,19 @@ def typeNeedsCx(type, retVal=False):
         return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)
     if retVal and type.isSpiderMonkeyInterface():
         return True
     return type.isCallback() or type.isAny() or type.isObject()
 
 # Returns a tuple consisting of a CGThing containing the type of the return
 # value, or None if there is no need for a return value, and a boolean signaling
 # whether the return value is passed in an out parameter.
+#
+# Whenever this is modified, please update CGExampleMember.getReturnType as
+# needed
 def getRetvalDeclarationForType(returnType, descriptorProvider,
                                 resultAlreadyAddRefed):
     if returnType is None or returnType.isVoid():
         # Nothing to declare
         return None, False
     if returnType.isPrimitive() and returnType.tag() in builtinNames:
         result = CGGeneric(builtinNames[returnType.tag()])
         if returnType.nullable():
@@ -3057,16 +3064,21 @@ def getRetvalDeclarationForType(returnTy
         return result, True
     raise TypeError("Don't know how to declare return value for %s" %
                     returnType)
 
 def isResultAlreadyAddRefed(descriptor, extendedAttributes):
     # Default to already_AddRefed on the main thread, raw pointer in workers
     return not descriptor.workers and not 'resultNotAddRefed' in extendedAttributes
 
+def needCx(returnType, arguments, extendedAttributes):
+    return (typeNeedsCx(returnType, True) or
+            any(typeNeedsCx(a.type) for (a, _) in arguments) or
+            'implicitJSContext' in extendedAttributes)
+
 class CGCallGenerator(CGThing):
     """
     A class to generate an actual call to a C++ object.  Assumes that the C++
     object is stored in a variable whose name is given by the |object| argument.
 
     errorReport should be a CGThing for an error report or None if no
     error reporting is needed.
     """
@@ -3093,19 +3105,17 @@ class CGCallGenerator(CGThing):
             args.append(CGGeneric(name))
 
         # Return values that go in outparams go here
         if resultOutParam:
             args.append(CGGeneric("result"))
         if isFallible:
             args.append(CGGeneric("rv"))
 
-        needsCx = (typeNeedsCx(returnType, True) or
-                   any(typeNeedsCx(a.type) for (a, _) in arguments) or
-                   'implicitJSContext' in extendedAttributes)
+        needsCx = needCx(returnType, arguments, extendedAttributes)
 
         if not "cx" in argsPre and needsCx:
             args.prepend(CGGeneric("cx"))
 
         # Build up our actual call
         self.cgRoot = CGList([], "\n")
 
         call = CGGeneric(nativeMethodName)
@@ -3532,16 +3542,20 @@ class FakeArgument():
         self.type = type
         self.optional = False
         self.variadic = False
         self.defaultValue = None
         self.treatNullAs = interfaceMember.treatNullAs
         self.treatUndefinedAs = interfaceMember.treatUndefinedAs
         self.enforceRange = False
         self.clamp = False
+        class FakeIdentifier():
+            def __init__(self):
+                self.name = "arg"
+        self.identifier = FakeIdentifier()
 
 class CGSetterCall(CGPerSignatureCall):
     """
     A class to generate a native object setter call for a particular IDL
     setter.
     """
     def __init__(self, argType, nativeMethodName, descriptor, attr):
         CGPerSignatureCall.__init__(self, None, [],
@@ -3632,21 +3646,26 @@ class CGSpecializedMethod(CGAbstractStat
         self.method = method
         name = method.identifier.name
         args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
                 Argument('%s*' % descriptor.nativeType, 'self'),
                 Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')]
         CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
 
     def definition_body(self):
-        name = self.method.identifier.name
-        nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+        nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
+                                                        self.method)
         return CGMethodCall([], nativeName, self.method.isStatic(),
                             self.descriptor, self.method).define()
 
+    @staticmethod
+    def makeNativeName(descriptor, method):
+        name = method.identifier.name
+        return MakeNativeName(descriptor.binaryNames.get(name, name))
+
 class CGGenericGetter(CGAbstractBindingMethod):
     """
     A class for generating the C++ code for an IDL attribute getter.
     """
     def __init__(self, descriptor, lenientThis=False):
         args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
                 Argument('JS::Value*', 'vp')]
         if lenientThis:
@@ -3677,29 +3696,33 @@ class CGSpecializedGetter(CGAbstractStat
         name = 'get_' + attr.identifier.name
         args = [ Argument('JSContext*', 'cx'),
                  Argument('JSHandleObject', 'obj'),
                  Argument('%s*' % descriptor.nativeType, 'self'),
                  Argument('JS::Value*', 'vp') ]
         CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
 
     def definition_body(self):
-        name = self.attr.identifier.name
-        nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+        nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
+                                                        self.attr)
+        return CGIndenter(CGGetterCall(self.attr.type, nativeName,
+                                       self.descriptor, self.attr)).define()
+
+    @staticmethod
+    def makeNativeName(descriptor, attr):
+        name = attr.identifier.name
+        nativeName = MakeNativeName(descriptor.binaryNames.get(name, name))
         # resultOutParam does not depend on whether resultAlreadyAddRefed is set
-        (_, resultOutParam) = getRetvalDeclarationForType(self.attr.type,
-                                                          self.descriptor,
+        (_, resultOutParam) = getRetvalDeclarationForType(attr.type, descriptor,
                                                           False)
         infallible = ('infallible' in
-                      self.descriptor.getExtendedAttributes(self.attr,
-                                                            getter=True))
-        if resultOutParam or self.attr.type.nullable() or not infallible:
+                      descriptor.getExtendedAttributes(attr, getter=True))
+        if resultOutParam or attr.type.nullable() or not infallible:
             nativeName = "Get" + nativeName
-        return CGIndenter(CGGetterCall(self.attr.type, nativeName,
-                                       self.descriptor, self.attr)).define()
+        return nativeName
 
 class CGGenericSetter(CGAbstractBindingMethod):
     """
     A class for generating the C++ code for an IDL attribute setter.
     """
     def __init__(self, descriptor, lenientThis=False):
         args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
                 Argument('JS::Value*', 'vp')]
@@ -3739,21 +3762,26 @@ class CGSpecializedSetter(CGAbstractStat
         name = 'set_' + attr.identifier.name
         args = [ Argument('JSContext*', 'cx'),
                  Argument('JSHandleObject', 'obj'),
                  Argument('%s*' % descriptor.nativeType, 'self'),
                  Argument('JS::Value*', 'argv')]
         CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
 
     def definition_body(self):
-        name = self.attr.identifier.name
-        nativeName = "Set" + MakeNativeName(self.descriptor.binaryNames.get(name, name))
+        nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
+                                                        self.attr)
         return CGIndenter(CGSetterCall(self.attr.type, nativeName,
                                        self.descriptor, self.attr)).define()
 
+    @staticmethod
+    def makeNativeName(descriptor, attr):
+        name = attr.identifier.name
+        return "Set" + MakeNativeName(descriptor.binaryNames.get(name, name))
+
 def memberIsCreator(member):
     return member.getExtendedAttribute("Creator") is not None
 
 class CGMemberJITInfo(CGThing):
     """
     A class for generating the JITInfo for a property that points to
     our specialized getter and setter.
     """
@@ -5775,16 +5803,408 @@ class CGBindingRoot(CGThing):
         # Store the final result.
         self.root = curr
 
     def declare(self):
         return stripTrailingWhitespace(self.root.declare())
     def define(self):
         return stripTrailingWhitespace(self.root.define())
 
+class CGExampleMember(CGThing):
+    def __init__(self, descriptor, member, name, signatures, extendedAttrs):
+        self.descriptor = descriptor
+        self.member = member
+        self.name = name
+        self.signatures = signatures
+        self.extendedAttrs = extendedAttrs
+        self.resultAlreadyAddRefed = isResultAlreadyAddRefed(self.descriptor,
+                                                             self.extendedAttrs)
+
+    def define(self):
+        static = "static " if self.member.isStatic() else ""
+        # Mark our getters, which are attrs that have a non-void return type,
+        # as const.
+        if self.member.isAttr() and not self.signatures[0][0].isVoid():
+            const = " const"
+        else:
+            const = ""
+        return "\n".join("%s%s %s(%s)%s;" %
+                         (static,
+                          self.getReturnType(s[0], False),
+                          self.name,
+                          self.getArgs(s[0], s[1]),
+                          const) for s in self.signatures)
+
+    def getReturnType(self, type, isMember):
+        if type.isVoid():
+            return "void"
+        if type.isPrimitive() and type.tag() in builtinNames:
+            result = CGGeneric(builtinNames[type.tag()])
+            if type.nullable():
+                result = CGWrapper(result, pre="Nullable<", post=">")
+            return result.define()
+        if type.isString():
+            if isMember:
+                return "nsString"
+            # Outparam
+            return "void"
+        if type.isEnum():
+            if type.nullable():
+                raise TypeError("We don't support nullable enum return values")
+            return type.inner.identifier.name
+        if type.isGeckoInterface():
+            nativeType = self.descriptor.getDescriptor(
+                type.unroll().inner.identifier.name).nativeType
+            # Now trim off unnecessary namespaces
+            nativeType = nativeType.split("::")
+            if nativeType[0] == "mozilla":
+                nativeType.pop(0)
+                if nativeType[0] == "dom":
+                    nativeType.pop(0)
+            result = CGGeneric("::".join(nativeType))
+            if self.resultAlreadyAddRefed:
+                if isMember:
+                    holder = "nsRefPtr"
+                else:
+                    holder = "already_AddRefed"
+                if memberIsCreator(self.member):
+                    warning = ""
+                else:
+                    warning = "// Mark this as resultNotAddRefed to return raw pointers\n"
+                result = CGWrapper(result,
+                                   pre=("%s%s<" % (warning, holder)),
+                                   post=">")
+            else:
+                result = CGWrapper(result, post="*")
+            return result.define()
+        if type.isCallback():
+            # XXXbz we're going to assume that callback types are always
+            # nullable for now.
+            return "JSObject*"
+        if type.isAny():
+            return "JS::Value"
+        if type.isObject() or type.isSpiderMonkeyInterface():
+            return "JSObject*"
+        if type.isSequence():
+            assert not isMember
+            # Outparam
+            return "void"
+        raise TypeError("Don't know how to declare return value for %s" %
+                        type)
+
+    def getArgs(self, returnType, argList):
+        args = [self.getArg(arg) for arg in argList]
+        # Now the outparams
+        if returnType.isString():
+            args.append("nsString& retval")
+        elif returnType.isSequence():
+            nullable = returnType.nullable()
+            if nullable:
+                returnType = returnType.inner
+            # And now the actual underlying type
+            elementDecl = self.getReturnType(returnType.inner, True)
+            type = CGWrapper(CGGeneric(elementDecl), pre="nsTArray< ", post=" >")
+            if nullable:
+                type = CGWrapper(type, pre="Nullable< ", post=" >")
+            args.append("%s& retval" % type.define())
+        # And the ErrorResult
+        if not 'infallible' in self.extendedAttrs:
+            args.append("ErrorResult& rv")
+        # And if we're static, a global
+        if self.member.isStatic():
+            args.insert(0, "nsISupports* global")
+        # And jscontext bits.  needCx expects a list of tuples, in each of which
+        # the first element is the actual argument
+        if needCx(returnType, ((a, "") for a in argList), self.extendedAttrs):
+            args.insert(0, "JSContext* cx")
+        return ", ".join(args)
+
+    def doGetArgType(self, type, optional, isMember):
+        """
+        The main work of getArgType.  Returns a string type decl, whether this
+        is a const ref, as well as whether the type should be wrapped in
+        Nullable as needed.
+        """
+        if type.isArray():
+            raise TypeError("Can't handle array arguments yet")
+
+        if type.isSequence():
+            nullable = type.nullable()
+            if nullable:
+                type = type.inner
+            elementType = type.inner
+            decl = CGWrapper(self.getArgType(elementType, False, True)[0],
+                             pre="Sequence< ", post=" >")
+            return decl.define(), True, True
+
+        if type.isUnion():
+            if type.nullable():
+                type = type.inner
+            return str(type), True, True
+
+        if type.isGeckoInterface():
+            iface = type.unroll().inner
+            argIsPointer = type.nullable() or iface.isExternal()
+            forceOwningType = iface.isCallback() or isMember
+            if argIsPointer:
+                if (optional or isMember) and forceOwningType:
+                    typeDecl = "nsRefPtr<%s>"
+                else:
+                    typeDecl = "%s*"
+            else:
+                if optional or isMember:
+                    if forceOwningType:
+                        typeDecl = "OwningNonNull<%s>"
+                    else:
+                        typeDecl = "NonNull<%s>"
+                else:
+                    typeDecl = "%s&"
+            ret