Merge autoland to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 07 Sep 2016 17:26:48 -0700
changeset 313073 7c655e03eef77b961ad44f62aaa0221b7cc51a43
parent 313072 ab70808cd4b6c6ad9a57a9f71cfa495fcea0aecd (current diff)
parent 313070 99ee305777f4fc9d884736e289d150102bbb6310 (diff)
child 313089 3f6286f7ce480c890cfe5a97149dfc86bff797b7
push id20479
push userkwierso@gmail.com
push dateThu, 08 Sep 2016 01:08:46 +0000
treeherderfx-team@fb7c6b034329 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone51.0a1
Merge autoland to central, a=merge
toolkit/components/places/UnifiedComplete.manifest
--- a/browser/components/extensions/schemas/history.json
+++ b/browser/components/extensions/schemas/history.json
@@ -86,29 +86,16 @@
             "type": "string",
             "description": "The visit ID of the referrer."
           },
           "transition": {
             "$ref": "TransitionType",
             "description": "The $(topic:transition-types)[transition type] for this visit from its referrer."
           }
         }
-      },
-      {
-        "id": "HistoryTime",
-        "description": "A time specified as a Date object, a number or string representing milliseconds since the epoch, or an ISO 8601 string",
-        "choices": [
-          {
-            "type": "string",
-            "pattern": "^[1-9]\\d*$"
-          },
-          {
-            "$ref": "extensionTypes.Date"
-          }
-        ]
       }
     ],
     "functions": [
       {
         "name": "search",
         "type": "function",
         "description": "Searches the history for the last visit time of each page matching the query.",
         "async": "callback",
@@ -117,22 +104,22 @@
             "name": "query",
             "type": "object",
             "properties": {
               "text": {
                 "type": "string",
                 "description": "A free-text query to the history service.  Leave empty to retrieve all pages."
               },
               "startTime": {
-                "$ref": "HistoryTime",
+                "$ref": "extensionTypes.Date",
                 "optional": true,
                 "description": "Limit results to those visited after this date. If not specified, this defaults to 24 hours in the past."
               },
               "endTime": {
-                "$ref": "HistoryTime",
+                "$ref": "extensionTypes.Date",
                 "optional": true,
                 "description": "Limit results to those visited before this date."
               },
               "maxResults": {
                 "type": "integer",
                 "optional": true,
                 "minimum": 1,
                 "description": "The maximum number of results to retrieve.  Defaults to 100."
@@ -205,17 +192,17 @@
                 "description": "The title of the page."
               },
               "transition": {
                 "$ref": "TransitionType",
                 "optional": true,
                 "description": "The $(topic:transition-types)[transition type] for this visit from its referrer."
               },
               "visitTime": {
-                "$ref": "HistoryTime",
+                "$ref": "extensionTypes.Date",
                 "optional": true,
                 "description": "The date when this visit occurred."
               }
             }
           },
           {
             "name": "callback",
             "type": "function",
@@ -254,21 +241,21 @@
         "description": "Removes all items within the specified date range from the history.  Pages will not be removed from the history unless all visits fall within the range.",
         "async": "callback",
         "parameters": [
           {
             "name": "range",
             "type": "object",
             "properties": {
               "startTime": {
-                "$ref": "HistoryTime",
+                "$ref": "extensionTypes.Date",
                 "description": "Items added to history after this date."
               },
               "endTime": {
-                "$ref": "HistoryTime",
+                "$ref": "extensionTypes.Date",
                 "description": "Items added to history before this date."
               }
             }
           },
           {
             "name": "callback",
             "type": "function",
             "parameters": []
--- a/browser/components/extensions/test/xpcshell/test_ext_history.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_history.js
@@ -284,17 +284,16 @@ add_task(function* test_add_url() {
 
     browser.test.sendMessage("ready");
   }
 
   let addTestData = [
     [{}, "default"],
     [{visitTime: new Date()}, "with_date"],
     [{visitTime: Date.now()}, "with_ms_number"],
-    [{visitTime: Date.now().toString()}, "with_ms_string"],
     [{visitTime: new Date().toISOString()}, "with_iso_string"],
     [{transition: "typed"}, "valid_transition"],
   ];
 
   let failTestData = [
     [{transition: "generated"}, "an invalid transition", "|generated| is not a supported transition for history"],
     [{visitTime: Date.now() + 1000000}, "a future date", "cannot be a future date"],
     [{url: "about.config"}, "an invalid url", "about.config is not a valid URL"],
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -162,17 +162,17 @@ static const nsAttrValue::EnumTable kMoz
   { "normal",             (int16_t)AudioChannel::Normal },
   { "content",            (int16_t)AudioChannel::Content },
   { "notification",       (int16_t)AudioChannel::Notification },
   { "alarm",              (int16_t)AudioChannel::Alarm },
   { "telephony",          (int16_t)AudioChannel::Telephony },
   { "ringer",             (int16_t)AudioChannel::Ringer },
   { "publicnotification", (int16_t)AudioChannel::Publicnotification },
   { "system",             (int16_t)AudioChannel::System },
-  { nullptr }
+  { nullptr,              0 }
 };
 
 /* static */ void
 AudioChannelService::CreateServiceIfNeeded()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!gAudioChannelService) {
--- a/dom/base/CORSMode.h
+++ b/dom/base/CORSMode.h
@@ -4,17 +4,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/. */
 
 #ifndef CORSMode_h_
 #define CORSMode_h_
 
 namespace mozilla {
 
-enum CORSMode {
+enum CORSMode : uint8_t {
   /**
    * The default of not using CORS to validate cross-origin loads.
    */
   CORS_NONE,
 
   /**
    * Validate cross-site loads using CORS, but do not send any credentials
    * (cookies, HTTP auth logins, etc) along with the request.
--- a/dom/base/DirectionalityUtils.h
+++ b/dom/base/DirectionalityUtils.h
@@ -17,17 +17,17 @@ class nsTextNode;
 namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 namespace mozilla {
 
-enum Directionality {
+enum Directionality : uint8_t {
   eDir_NotSet,
   eDir_RTL,
   eDir_LTR,
   eDir_Auto
 };
 
 /**
  * Set the directionality of an element according to the algorithm defined at
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -3264,17 +3264,17 @@ Element::Matches(const nsAString& aSelec
                                                  selectorList);
 }
 
 static const nsAttrValue::EnumTable kCORSAttributeTable[] = {
   // Order matters here
   // See ParseCORSValue
   { "anonymous",       CORS_ANONYMOUS       },
   { "use-credentials", CORS_USE_CREDENTIALS },
-  { 0 }
+  { nullptr,           0 }
 };
 
 /* static */ void
 Element::ParseCORSValue(const nsAString& aValue,
                         nsAttrValue& aResult)
 {
   DebugOnly<bool> success =
     aResult.ParseEnumValue(aValue, kCORSAttributeTable, false,
--- a/dom/base/nsAttrValue.h
+++ b/dom/base/nsAttrValue.h
@@ -7,28 +7,31 @@
 /*
  * A struct that represents the value (type and actual data) of an
  * attribute.
  */
 
 #ifndef nsAttrValue_h___
 #define nsAttrValue_h___
 
+#include <type_traits>
+
 #include "nscore.h"
 #include "nsStringGlue.h"
 #include "nsStringBuffer.h"
 #include "nsColor.h"
 #include "nsCaseTreatment.h"
 #include "nsMargin.h"
 #include "nsCOMPtr.h"
 #include "SVGAttrValueWrapper.h"
 #include "nsTArrayForwardDeclare.h"
 #include "nsIAtom.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/EnumTypeTraits.h"
 
 // Undefine LoadImage to prevent naming conflict with Windows.
 #undef LoadImage
 
 class nsAString;
 class nsIDocument;
 class nsStyledElement;
 struct MiscContainer;
@@ -256,20 +259,39 @@ public:
 
   /**
    * Structure for a mapping from int (enum) values to strings.  When you use
    * it you generally create an array of them.
    * Instantiate like this:
    * EnumTable myTable[] = {
    *   { "string1", 1 },
    *   { "string2", 2 },
-   *   { 0 }
+   *   { nullptr, 0 }
    * }
    */
   struct EnumTable {
+    // EnumTable can be initialized either with an int16_t value
+    // or a value of an enumeration type that can fit within an int16_t.
+
+    constexpr EnumTable(const char* aTag, int16_t aValue)
+      : tag(aTag)
+      , value(aValue)
+    {
+    }
+
+    template<typename T,
+             typename = typename std::enable_if<std::is_enum<T>::value>::type>
+    constexpr EnumTable(const char* aTag, T aValue)
+      : tag(aTag)
+      , value(static_cast<int16_t>(aValue))
+    {
+      static_assert(mozilla::EnumTypeFitsWithin<T, int16_t>::value,
+                    "aValue must be an enum that fits within int16_t");
+    }
+
     /** The string the value maps to */
     const char* tag;
     /** The enum value that maps to this string */
     int16_t value;
   };
 
   /**
    * Parse into an enum value.
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -299,36 +299,36 @@ bool nsContentUtils::sFragmentParsingAct
 bool nsContentUtils::sDOMWindowDumpEnabled;
 #endif
 
 bool nsContentUtils::sDoNotTrackEnabled = false;
 
 mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
 
 // Subset of http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
-enum AutocompleteFieldName
+enum AutocompleteFieldName : uint8_t
 {
   #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
     eAutocompleteFieldName_##name_,
   #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
     AUTOCOMPLETE_FIELD_NAME(name_, value_)
   #include "AutocompleteFieldList.h"
   #undef AUTOCOMPLETE_FIELD_NAME
   #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
 };
 
-enum AutocompleteFieldHint
+enum AutocompleteFieldHint : uint8_t
 {
   #define AUTOCOMPLETE_FIELD_HINT(name_, value_) \
     eAutocompleteFieldHint_##name_,
   #include "AutocompleteFieldList.h"
   #undef AUTOCOMPLETE_FIELD_HINT
 };
 
-enum AutocompleteFieldContactHint
+enum AutocompleteFieldContactHint : uint8_t
 {
   #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
     eAutocompleteFieldContactHint_##name_,
   #include "AutocompleteFieldList.h"
   #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
 };
 
 enum AutocompleteCategory
@@ -338,41 +338,41 @@ enum AutocompleteCategory
   #undef AUTOCOMPLETE_CATEGORY
 };
 
 static const nsAttrValue::EnumTable kAutocompleteFieldNameTable[] = {
   #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
     { value_, eAutocompleteFieldName_##name_ },
   #include "AutocompleteFieldList.h"
   #undef AUTOCOMPLETE_FIELD_NAME
-  { 0 }
+  { nullptr, 0 }
 };
 
 static const nsAttrValue::EnumTable kAutocompleteContactFieldNameTable[] = {
   #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
     { value_, eAutocompleteFieldName_##name_ },
   #include "AutocompleteFieldList.h"
   #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
-  { 0 }
+  { nullptr, 0 }
 };
 
 static const nsAttrValue::EnumTable kAutocompleteFieldHintTable[] = {
   #define AUTOCOMPLETE_FIELD_HINT(name_, value_) \
     { value_, eAutocompleteFieldHint_##name_ },
   #include "AutocompleteFieldList.h"
   #undef AUTOCOMPLETE_FIELD_HINT
-  { 0 }
+  { nullptr, 0 }
 };
 
 static const nsAttrValue::EnumTable kAutocompleteContactFieldHintTable[] = {
   #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
     { value_, eAutocompleteFieldContactHint_##name_ },
   #include "AutocompleteFieldList.h"
   #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
-  { 0 }
+  { nullptr, 0 }
 };
 
 namespace {
 
 static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
 
 static PLDHashTable* sEventListenerManagersHash;
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -12316,16 +12316,17 @@ class CGDictionary(CGThing):
         if memberInits:
             body += fill(
                 """
                 bool isNull = val.isNullOrUndefined();
                 // We only need these if !isNull, in which case we have |cx|.
                 Maybe<JS::Rooted<JSObject *> > object;
                 Maybe<JS::Rooted<JS::Value> > temp;
                 if (!isNull) {
+                  MOZ_ASSERT(cx);
                   object.emplace(cx, &val.toObject());
                   temp.emplace(cx);
                 }
                 $*{memberInits}
                 """,
                 memberInits="\n".join(memberInits))
 
         body += "return true;\n"
--- a/dom/html/HTMLBRElement.cpp
+++ b/dom/html/HTMLBRElement.cpp
@@ -25,21 +25,21 @@ HTMLBRElement::HTMLBRElement(already_Add
 
 HTMLBRElement::~HTMLBRElement()
 {
 }
 
 NS_IMPL_ELEMENT_CLONE(HTMLBRElement)
 
 static const nsAttrValue::EnumTable kClearTable[] = {
-  { "left", NS_STYLE_CLEAR_LEFT },
-  { "right", NS_STYLE_CLEAR_RIGHT },
-  { "all", NS_STYLE_CLEAR_BOTH },
-  { "both", NS_STYLE_CLEAR_BOTH },
-  { 0 }
+  { "left", StyleClear::Left },
+  { "right", StyleClear::Right },
+  { "all", StyleClear::Both },
+  { "both", StyleClear::Both },
+  { nullptr, 0 }
 };
 
 bool
 HTMLBRElement::ParseAttribute(int32_t aNamespaceID,
                               nsIAtom* aAttribute,
                               const nsAString& aValue,
                               nsAttrValue& aResult)
 {
--- a/dom/html/HTMLButtonElement.cpp
+++ b/dom/html/HTMLButtonElement.cpp
@@ -42,17 +42,17 @@ NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER
 
 namespace mozilla {
 namespace dom {
 
 static const nsAttrValue::EnumTable kButtonTypeTable[] = {
   { "button", NS_FORM_BUTTON_BUTTON },
   { "reset", NS_FORM_BUTTON_RESET },
   { "submit", NS_FORM_BUTTON_SUBMIT },
-  { 0 }
+  { nullptr, 0 }
 };
 
 // Default type is 'submit'.
 static const nsAttrValue::EnumTable* kButtonDefaultType = &kButtonTypeTable[2];
 
 
 // Construction, destruction
 HTMLButtonElement::HTMLButtonElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -82,17 +82,17 @@ namespace mozilla {
 namespace dom {
 
 static const uint8_t NS_FORM_AUTOCOMPLETE_ON  = 1;
 static const uint8_t NS_FORM_AUTOCOMPLETE_OFF = 0;
 
 static const nsAttrValue::EnumTable kFormAutocompleteTable[] = {
   { "on",  NS_FORM_AUTOCOMPLETE_ON },
   { "off", NS_FORM_AUTOCOMPLETE_OFF },
-  { 0 }
+  { nullptr, 0 }
 };
 // Default autocomplete value is 'on'.
 static const nsAttrValue::EnumTable* kFormDefaultAutocomplete = &kFormAutocompleteTable[0];
 
 bool HTMLFormElement::gFirstFormSubmitted = false;
 bool HTMLFormElement::gPasswordManagerInitialized = false;
 
 HTMLFormElement::HTMLFormElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
--- a/dom/html/HTMLFormSubmissionConstants.h
+++ b/dom/html/HTMLFormSubmissionConstants.h
@@ -7,25 +7,25 @@
 #ifndef mozilla_dom_HTMLFormSubmissionConstants_h
 #define mozilla_dom_HTMLFormSubmissionConstants_h
 
 #include "nsIForm.h"
 
 static const nsAttrValue::EnumTable kFormMethodTable[] = {
   { "get", NS_FORM_METHOD_GET },
   { "post", NS_FORM_METHOD_POST },
-  { 0 }
+  { nullptr, 0 }
 };
 
 // Default method is 'get'.
 static const nsAttrValue::EnumTable* kFormDefaultMethod = &kFormMethodTable[0];
 
 static const nsAttrValue::EnumTable kFormEnctypeTable[] = {
   { "multipart/form-data", NS_FORM_ENCTYPE_MULTIPART },
   { "application/x-www-form-urlencoded", NS_FORM_ENCTYPE_URLENCODED },
   { "text/plain", NS_FORM_ENCTYPE_TEXTPLAIN },
-  { 0 }
+  { nullptr, 0 }
 };
 
 // Default method is 'application/x-www-form-urlencoded'.
 static const nsAttrValue::EnumTable* kFormDefaultEnctype = &kFormEnctypeTable[1];
 
 #endif // mozilla_dom_HTMLFormSubmissionConstants_h
--- a/dom/html/HTMLHRElement.cpp
+++ b/dom/html/HTMLHRElement.cpp
@@ -38,17 +38,17 @@ HTMLHRElement::ParseAttribute(int32_t aN
                               nsIAtom* aAttribute,
                               const nsAString& aValue,
                               nsAttrValue& aResult)
 {
   static const nsAttrValue::EnumTable kAlignTable[] = {
     { "left", NS_STYLE_TEXT_ALIGN_LEFT },
     { "right", NS_STYLE_TEXT_ALIGN_RIGHT },
     { "center", NS_STYLE_TEXT_ALIGN_CENTER },
-    { 0 }
+    { nullptr, 0 }
   };
 
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::width) {
       return aResult.ParseSpecialIntValue(aValue);
     }
     if (aAttribute == nsGkAtoms::size) {
       return aResult.ParseIntWithBounds(aValue, 1, 1000);
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -173,17 +173,17 @@ static const nsAttrValue::EnumTable kInp
   { "range", NS_FORM_INPUT_RANGE },
   { "search", NS_FORM_INPUT_SEARCH },
   { "submit", NS_FORM_INPUT_SUBMIT },
   { "tel", NS_FORM_INPUT_TEL },
   { "text", NS_FORM_INPUT_TEXT },
   { "time", NS_FORM_INPUT_TIME },
   { "url", NS_FORM_INPUT_URL },
   { "week", NS_FORM_INPUT_WEEK },
-  { 0 }
+  { nullptr, 0 }
 };
 
 // Default type is 'text'.
 static const nsAttrValue::EnumTable* kInputDefaultType = &kInputTypeTable[17];
 
 static const uint8_t NS_INPUT_INPUTMODE_AUTO              = 0;
 static const uint8_t NS_INPUT_INPUTMODE_NUMERIC           = 1;
 static const uint8_t NS_INPUT_INPUTMODE_DIGIT             = 2;
@@ -195,17 +195,17 @@ static const uint8_t NS_INPUT_INPUTMODE_
 static const nsAttrValue::EnumTable kInputInputmodeTable[] = {
   { "auto", NS_INPUT_INPUTMODE_AUTO },
   { "numeric", NS_INPUT_INPUTMODE_NUMERIC },
   { "digit", NS_INPUT_INPUTMODE_DIGIT },
   { "uppercase", NS_INPUT_INPUTMODE_UPPERCASE },
   { "lowercase", NS_INPUT_INPUTMODE_LOWERCASE },
   { "titlecase", NS_INPUT_INPUTMODE_TITLECASE },
   { "autocapitalized", NS_INPUT_INPUTMODE_AUTOCAPITALIZED },
-  { 0 }
+  { nullptr, 0 }
 };
 
 // Default inputmode value is "auto".
 static const nsAttrValue::EnumTable* kInputDefaultInputmode = &kInputInputmodeTable[0];
 
 const Decimal HTMLInputElement::kStepScaleFactorDate = Decimal(86400000);
 const Decimal HTMLInputElement::kStepScaleFactorNumberRange = Decimal(1);
 const Decimal HTMLInputElement::kStepScaleFactorTime = Decimal(1000);
--- a/dom/html/HTMLLIElement.cpp
+++ b/dom/html/HTMLLIElement.cpp
@@ -31,27 +31,27 @@ NS_IMPL_STRING_ATTR(HTMLLIElement, Type,
 NS_IMPL_INT_ATTR(HTMLLIElement, Value, value)
 
 // values that are handled case-insensitively
 static const nsAttrValue::EnumTable kUnorderedListItemTypeTable[] = {
   { "disc", NS_STYLE_LIST_STYLE_DISC },
   { "circle", NS_STYLE_LIST_STYLE_CIRCLE },
   { "round", NS_STYLE_LIST_STYLE_CIRCLE },
   { "square", NS_STYLE_LIST_STYLE_SQUARE },
-  { 0 }
+  { nullptr, 0 }
 };
 
 // values that are handled case-sensitively
 static const nsAttrValue::EnumTable kOrderedListItemTypeTable[] = {
   { "A", NS_STYLE_LIST_STYLE_UPPER_ALPHA },
   { "a", NS_STYLE_LIST_STYLE_LOWER_ALPHA },
   { "I", NS_STYLE_LIST_STYLE_UPPER_ROMAN },
   { "i", NS_STYLE_LIST_STYLE_LOWER_ROMAN },
   { "1", NS_STYLE_LIST_STYLE_DECIMAL },
-  { 0 }
+  { nullptr, 0 }
 };
 
 bool
 HTMLLIElement::ParseAttribute(int32_t aNamespaceID,
                               nsIAtom* aAttribute,
                               const nsAString& aValue,
                               nsAttrValue& aResult)
 {
--- a/dom/html/HTMLLegendElement.cpp
+++ b/dom/html/HTMLLegendElement.cpp
@@ -42,17 +42,17 @@ HTMLLegendElement::ParseAttribute(int32_
 {
   // this contains center, because IE4 does
   static const nsAttrValue::EnumTable kAlignTable[] = {
     { "left", NS_STYLE_TEXT_ALIGN_LEFT },
     { "right", NS_STYLE_TEXT_ALIGN_RIGHT },
     { "center", NS_STYLE_TEXT_ALIGN_CENTER },
     { "bottom", NS_STYLE_VERTICAL_ALIGN_BOTTOM },
     { "top", NS_STYLE_VERTICAL_ALIGN_TOP },
-    { 0 }
+    { nullptr, 0 }
   };
 
   if (aAttribute == nsGkAtoms::align && aNamespaceID == kNameSpaceID_None) {
     return aResult.ParseEnumValue(aValue, kAlignTable, false);
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2539,33 +2539,28 @@ HTMLMediaElement::AddCaptureMediaTrackTo
        track.get(), destinationTrackID, inputTrack, port.get()));
 }
 
 already_AddRefed<DOMMediaStream>
 HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded,
                                         bool aCaptureAudio,
                                         MediaStreamGraph* aGraph)
 {
+  MOZ_RELEASE_ASSERT(aGraph);
+
   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
   if (!window) {
     return nullptr;
   }
 #ifdef MOZ_EME
   if (ContainsRestrictedContent()) {
     return nullptr;
   }
 #endif
 
-  if (!aGraph) {
-    MediaStreamGraph::GraphDriverType graphDriverType =
-      HasAudio() ? MediaStreamGraph::AUDIO_THREAD_DRIVER
-                 : MediaStreamGraph::SYSTEM_THREAD_DRIVER;
-    aGraph = MediaStreamGraph::GetInstance(graphDriverType, mAudioChannel);
-  }
-
   if (!mOutputStreams.IsEmpty() &&
       aGraph != mOutputStreams[0].mStream->GetInputStream()->Graph()) {
     return nullptr;
   }
 
   OutputMediaStream* out = mOutputStreams.AppendElement();
   MediaStreamTrackSourceGetter* getter = new CaptureStreamTrackSourceGetter(this);
   out->mStream = DOMMediaStream::CreateTrackUnionStreamAsInput(window, aGraph, getter);
@@ -2650,43 +2645,58 @@ HTMLMediaElement::CaptureStreamInternal(
   RefPtr<DOMMediaStream> result = out->mStream;
   return result.forget();
 }
 
 already_AddRefed<DOMMediaStream>
 HTMLMediaElement::CaptureAudio(ErrorResult& aRv,
                                MediaStreamGraph* aGraph)
 {
-  RefPtr<DOMMediaStream> stream = CaptureStreamInternal(false, aGraph);
+  MOZ_RELEASE_ASSERT(aGraph);
+
+  RefPtr<DOMMediaStream> stream =
+    CaptureStreamInternal(false, true, aGraph);
   if (!stream) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   return stream.forget();
 }
 
 already_AddRefed<DOMMediaStream>
-HTMLMediaElement::MozCaptureStream(ErrorResult& aRv,
-                                   MediaStreamGraph* aGraph)
-{
-  RefPtr<DOMMediaStream> stream = CaptureStreamInternal(false, aGraph);
+HTMLMediaElement::MozCaptureStream(ErrorResult& aRv)
+{
+  MediaStreamGraph::GraphDriverType graphDriverType =
+    HasAudio() ? MediaStreamGraph::AUDIO_THREAD_DRIVER
+               : MediaStreamGraph::SYSTEM_THREAD_DRIVER;
+  MediaStreamGraph* graph =
+    MediaStreamGraph::GetInstance(graphDriverType, mAudioChannel);
+
+  RefPtr<DOMMediaStream> stream =
+    CaptureStreamInternal(false, false, graph);
   if (!stream) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   return stream.forget();
 }
 
 already_AddRefed<DOMMediaStream>
-HTMLMediaElement::MozCaptureStreamUntilEnded(ErrorResult& aRv,
-                                             MediaStreamGraph* aGraph)
-{
-  RefPtr<DOMMediaStream> stream = CaptureStreamInternal(true, aGraph);
+HTMLMediaElement::MozCaptureStreamUntilEnded(ErrorResult& aRv)
+{
+  MediaStreamGraph::GraphDriverType graphDriverType =
+    HasAudio() ? MediaStreamGraph::AUDIO_THREAD_DRIVER
+               : MediaStreamGraph::SYSTEM_THREAD_DRIVER;
+  MediaStreamGraph* graph =
+    MediaStreamGraph::GetInstance(graphDriverType, mAudioChannel);
+
+  RefPtr<DOMMediaStream> stream =
+    CaptureStreamInternal(true, false, graph);
   if (!stream) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   return stream.forget();
 }
 
@@ -3216,17 +3226,17 @@ bool HTMLMediaElement::ParseAttribute(in
                                       nsAttrValue& aResult)
 {
   // Mappings from 'preload' attribute strings to an enumeration.
   static const nsAttrValue::EnumTable kPreloadTable[] = {
     { "",         HTMLMediaElement::PRELOAD_ATTR_EMPTY },
     { "none",     HTMLMediaElement::PRELOAD_ATTR_NONE },
     { "metadata", HTMLMediaElement::PRELOAD_ATTR_METADATA },
     { "auto",     HTMLMediaElement::PRELOAD_ATTR_AUTO },
-    { 0 }
+    { nullptr,    0 }
   };
 
   if (aNamespaceID == kNameSpaceID_None) {
     if (ParseImageAttribute(aAttribute, aValue, aResult)) {
       return true;
     }
     if (aAttribute == nsGkAtoms::crossorigin) {
       ParseCORSValue(aValue, aResult);
@@ -6569,17 +6579,18 @@ HTMLMediaElement::AudioCaptureStreamChan
     uint64_t id = window->WindowID();
     MediaStreamGraph* msg =
       MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER,
                                     mAudioChannel);
 
     if (GetSrcMediaStream()) {
       mCaptureStreamPort = msg->ConnectToCaptureStream(id, GetSrcMediaStream());
     } else {
-      RefPtr<DOMMediaStream> stream = CaptureStreamInternal(false, msg);
+      RefPtr<DOMMediaStream> stream =
+        CaptureStreamInternal(false, false, msg);
       mCaptureStreamPort = msg->ConnectToCaptureStream(id, stream->GetPlaybackStream());
     }
   } else if (!mAudioCapturedByWindow && mCaptureStreamPort) {
     if (mDecoder) {
       ProcessedMediaStream* ps =
         mCaptureStreamPort->GetSource()->AsProcessedStream();
       MOZ_ASSERT(ps);
 
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -650,23 +650,21 @@ public:
   void CannotDecryptWaitingForKey();
 
   bool MozAutoplayEnabled() const
   {
     return mAutoplayEnabled;
   }
 
   already_AddRefed<DOMMediaStream> CaptureAudio(ErrorResult& aRv,
-                                                MediaStreamGraph* aGraph = nullptr);
+                                                MediaStreamGraph* aGraph);
 
-  already_AddRefed<DOMMediaStream> MozCaptureStream(ErrorResult& aRv,
-                                                    MediaStreamGraph* aGraph = nullptr);
+  already_AddRefed<DOMMediaStream> MozCaptureStream(ErrorResult& aRv);
 
-  already_AddRefed<DOMMediaStream> MozCaptureStreamUntilEnded(ErrorResult& aRv,
-                                                              MediaStreamGraph* aGraph = nullptr);
+  already_AddRefed<DOMMediaStream> MozCaptureStreamUntilEnded(ErrorResult& aRv);
 
   bool MozAudioCaptured() const
   {
     return mAudioCaptured;
   }
 
   void MozGetMetadata(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
                       ErrorResult& aRv);
@@ -892,17 +890,17 @@ protected:
    * The stream will never finish.
    *
    * When aCaptureAudio is true, we stop playout of audio and instead route it
    * to the DOMMediaStream. Volume and mute state will be applied to the audio
    * reaching the stream. No video tracks will be captured in this case.
    */
   already_AddRefed<DOMMediaStream> CaptureStreamInternal(bool aFinishWhenEnded,
                                                          bool aCaptureAudio,
-                                                         MediaStreamGraph* aGraph = nullptr);
+                                                         MediaStreamGraph* aGraph);
 
   /**
    * Initialize a decoder as a clone of an existing decoder in another
    * element.
    * mLoadingSrc must already be set.
    */
   nsresult InitializeDecoderAsClone(MediaDecoder* aOriginal);
 
@@ -1034,17 +1032,17 @@ protected:
   /**
    * Called when "xpcom-shutdown" event is received.
    */
   void NotifyShutdownEvent();
 
   /**
    * Possible values of the 'preload' attribute.
    */
-  enum PreloadAttrValue {
+  enum PreloadAttrValue : uint8_t {
     PRELOAD_ATTR_EMPTY,    // set to ""
     PRELOAD_ATTR_NONE,     // set to "none"
     PRELOAD_ATTR_METADATA, // set to "metadata"
     PRELOAD_ATTR_AUTO      // set to "auto"
   };
 
   /**
    * The preloading action to perform. These dictate how we react to the
--- a/dom/html/HTMLMenuElement.cpp
+++ b/dom/html/HTMLMenuElement.cpp
@@ -17,28 +17,28 @@
 
 #define HTMLMENUBUILDER_CONTRACTID "@mozilla.org/content/html-menu-builder;1"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Menu)
 
 namespace mozilla {
 namespace dom {
 
-enum MenuType
+enum MenuType : uint8_t
 {
   MENU_TYPE_CONTEXT = 1,
   MENU_TYPE_TOOLBAR,
   MENU_TYPE_LIST
 };
 
 static const nsAttrValue::EnumTable kMenuTypeTable[] = {
   { "context", MENU_TYPE_CONTEXT },
   { "toolbar", MENU_TYPE_TOOLBAR },
   { "list", MENU_TYPE_LIST },
-  { 0 }
+  { nullptr, 0 }
 };
 
 static const nsAttrValue::EnumTable* kMenuDefaultType =
   &kMenuTypeTable[2];
 
 enum SeparatorType
 {
   ST_TRUE_INIT = -1,
--- a/dom/html/HTMLMenuItemElement.cpp
+++ b/dom/html/HTMLMenuItemElement.cpp
@@ -19,28 +19,28 @@ namespace mozilla {
 namespace dom {
 
 // First bits are needed for the menuitem type.
 #define NS_CHECKED_IS_TOGGLED (1 << 2)
 #define NS_ORIGINAL_CHECKED_VALUE (1 << 3)
 #define NS_MENUITEM_TYPE(bits) ((bits) & ~( \
   NS_CHECKED_IS_TOGGLED | NS_ORIGINAL_CHECKED_VALUE))
 
-enum CmdType                                                                 
-{                                                                            
+enum CmdType : uint8_t
+{
   CMD_TYPE_MENUITEM = 1,
   CMD_TYPE_CHECKBOX,
   CMD_TYPE_RADIO
 };
 
 static const nsAttrValue::EnumTable kMenuItemTypeTable[] = {
   { "menuitem", CMD_TYPE_MENUITEM },
   { "checkbox", CMD_TYPE_CHECKBOX },
   { "radio", CMD_TYPE_RADIO },
-  { 0 }
+  { nullptr, 0 }
 };
 
 static const nsAttrValue::EnumTable* kMenuItemDefaultType =
   &kMenuItemTypeTable[0];
 
 // A base class inherited by all radio visitors.
 class Visitor
 {
--- a/dom/html/HTMLSharedListElement.cpp
+++ b/dom/html/HTMLSharedListElement.cpp
@@ -50,26 +50,26 @@ nsAttrValue::EnumTable kListTypeTable[] 
   { "circle", NS_STYLE_LIST_STYLE_CIRCLE },
   { "round", NS_STYLE_LIST_STYLE_CIRCLE },
   { "square", NS_STYLE_LIST_STYLE_SQUARE },
   { "decimal", NS_STYLE_LIST_STYLE_DECIMAL },
   { "lower-roman", NS_STYLE_LIST_STYLE_LOWER_ROMAN },
   { "upper-roman", NS_STYLE_LIST_STYLE_UPPER_ROMAN },
   { "lower-alpha", NS_STYLE_LIST_STYLE_LOWER_ALPHA },
   { "upper-alpha", NS_STYLE_LIST_STYLE_UPPER_ALPHA },
-  { 0 }
+  { nullptr, 0 }
 };
 
 static const nsAttrValue::EnumTable kOldListTypeTable[] = {
   { "1", NS_STYLE_LIST_STYLE_DECIMAL },
   { "A", NS_STYLE_LIST_STYLE_UPPER_ALPHA },
   { "a", NS_STYLE_LIST_STYLE_LOWER_ALPHA },
   { "I", NS_STYLE_LIST_STYLE_UPPER_ROMAN },
   { "i", NS_STYLE_LIST_STYLE_LOWER_ROMAN },
-  { 0 }
+  { nullptr, 0 }
 };
 
 bool
 HTMLSharedListElement::ParseAttribute(int32_t aNamespaceID,
                                       nsIAtom* aAttribute,
                                       const nsAString& aValue,
                                       nsAttrValue& aResult)
 {
--- a/dom/html/HTMLTableCaptionElement.cpp
+++ b/dom/html/HTMLTableCaptionElement.cpp
@@ -27,17 +27,17 @@ HTMLTableCaptionElement::WrapNode(JSCont
 
 NS_IMPL_ELEMENT_CLONE(HTMLTableCaptionElement)
 
 static const nsAttrValue::EnumTable kCaptionAlignTable[] = {
   { "left",   NS_STYLE_CAPTION_SIDE_LEFT },
   { "right",  NS_STYLE_CAPTION_SIDE_RIGHT },
   { "top",    NS_STYLE_CAPTION_SIDE_TOP },
   { "bottom", NS_STYLE_CAPTION_SIDE_BOTTOM },
-  { 0 }
+  { nullptr,  0 }
 };
 
 bool
 HTMLTableCaptionElement::ParseAttribute(int32_t aNamespaceID,
                                         nsIAtom* aAttribute,
                                         const nsAString& aValue,
                                         nsAttrValue& aResult)
 {
--- a/dom/html/HTMLTableCellElement.cpp
+++ b/dom/html/HTMLTableCellElement.cpp
@@ -361,17 +361,17 @@ HTMLTableCellElement::GetAlign(DOMString
   }
 }
 
 static const nsAttrValue::EnumTable kCellScopeTable[] = {
   { "row",      NS_STYLE_CELL_SCOPE_ROW },
   { "col",      NS_STYLE_CELL_SCOPE_COL },
   { "rowgroup", NS_STYLE_CELL_SCOPE_ROWGROUP },
   { "colgroup", NS_STYLE_CELL_SCOPE_COLGROUP },
-  { 0 }
+  { nullptr,    0 }
 };
 
 void
 HTMLTableCellElement::GetScope(DOMString& aScope)
 {
   GetEnumAttr(nsGkAtoms::scope, nullptr, aScope);
 }
 
--- a/dom/html/HTMLTrackElement.cpp
+++ b/dom/html/HTMLTrackElement.cpp
@@ -62,17 +62,17 @@ namespace dom {
 
 // Map html attribute string values to TextTrackKind enums.
 static constexpr nsAttrValue::EnumTable kKindTable[] = {
   { "subtitles", static_cast<int16_t>(TextTrackKind::Subtitles) },
   { "captions", static_cast<int16_t>(TextTrackKind::Captions) },
   { "descriptions", static_cast<int16_t>(TextTrackKind::Descriptions) },
   { "chapters", static_cast<int16_t>(TextTrackKind::Chapters) },
   { "metadata", static_cast<int16_t>(TextTrackKind::Metadata) },
-  { 0 }
+  { nullptr, 0 }
 };
 
 // Invalid values are treated as "metadata" in ParseAttribute, but if no value
 // at all is specified, it's treated as "subtitles" in GetKind
 static constexpr const nsAttrValue::EnumTable* kKindTableInvalidValueDefault = &kKindTable[4];
 
 class WindowDestroyObserver final : public nsIObserver
 {
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -215,17 +215,17 @@ nsGenericHTMLElement::GetDataset(nsISupp
   *aDataset = Dataset().take();
   return NS_OK;
 }
 
 static const nsAttrValue::EnumTable kDirTable[] = {
   { "ltr", eDir_LTR },
   { "rtl", eDir_RTL },
   { "auto", eDir_Auto },
-  { 0 }
+  { nullptr, 0 }
 };
 
 void
 nsGenericHTMLElement::GetAccessKeyLabel(nsString& aLabel)
 {
   nsAutoString suffix;
   GetAccessKey(suffix);
   if (!suffix.IsEmpty()) {
@@ -1053,44 +1053,44 @@ nsGenericHTMLElement::GetPresContext(Pre
 }
 
 static const nsAttrValue::EnumTable kDivAlignTable[] = {
   { "left", NS_STYLE_TEXT_ALIGN_MOZ_LEFT },
   { "right", NS_STYLE_TEXT_ALIGN_MOZ_RIGHT },
   { "center", NS_STYLE_TEXT_ALIGN_MOZ_CENTER },
   { "middle", NS_STYLE_TEXT_ALIGN_MOZ_CENTER },
   { "justify", NS_STYLE_TEXT_ALIGN_JUSTIFY },
-  { 0 }
+  { nullptr, 0 }
 };
 
 static const nsAttrValue::EnumTable kFrameborderTable[] = {
   { "yes", NS_STYLE_FRAME_YES },
   { "no", NS_STYLE_FRAME_NO },
   { "1", NS_STYLE_FRAME_1 },
   { "0", NS_STYLE_FRAME_0 },
-  { 0 }
+  { nullptr, 0 }
 };
 
 static const nsAttrValue::EnumTable kScrollingTable[] = {
   { "yes", NS_STYLE_FRAME_YES },
   { "no", NS_STYLE_FRAME_NO },
   { "on", NS_STYLE_FRAME_ON },
   { "off", NS_STYLE_FRAME_OFF },
   { "scroll", NS_STYLE_FRAME_SCROLL },
   { "noscroll", NS_STYLE_FRAME_NOSCROLL },
   { "auto", NS_STYLE_FRAME_AUTO },
-  { 0 }
+  { nullptr, 0 }
 };
 
 static const nsAttrValue::EnumTable kTableVAlignTable[] = {
   { "top",     NS_STYLE_VERTICAL_ALIGN_TOP },
   { "middle",  NS_STYLE_VERTICAL_ALIGN_MIDDLE },
   { "bottom",  NS_STYLE_VERTICAL_ALIGN_BOTTOM },
   { "baseline",NS_STYLE_VERTICAL_ALIGN_BASELINE },
-  { 0 }
+  { nullptr,   0 }
 };
 
 bool
 nsGenericHTMLElement::ParseAlignValue(const nsAString& aString,
                                       nsAttrValue& aResult)
 {
   static const nsAttrValue::EnumTable kAlignTable[] = {
     { "left",      NS_STYLE_TEXT_ALIGN_LEFT },
@@ -1102,31 +1102,31 @@ nsGenericHTMLElement::ParseAlignValue(co
 
     { "center",    NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE },
     { "baseline",  NS_STYLE_VERTICAL_ALIGN_BASELINE },
 
     { "texttop",   NS_STYLE_VERTICAL_ALIGN_TEXT_TOP },
     { "absmiddle", NS_STYLE_VERTICAL_ALIGN_MIDDLE },
     { "abscenter", NS_STYLE_VERTICAL_ALIGN_MIDDLE },
     { "absbottom", NS_STYLE_VERTICAL_ALIGN_BOTTOM },
-    { 0 }
+    { nullptr,     0 }
   };
 
   return aResult.ParseEnumValue(aString, kAlignTable, false);
 }
 
 //----------------------------------------
 
 static const nsAttrValue::EnumTable kTableHAlignTable[] = {
   { "left",   NS_STYLE_TEXT_ALIGN_LEFT },
   { "right",  NS_STYLE_TEXT_ALIGN_RIGHT },
   { "center", NS_STYLE_TEXT_ALIGN_CENTER },
   { "char",   NS_STYLE_TEXT_ALIGN_CHAR },
   { "justify",NS_STYLE_TEXT_ALIGN_JUSTIFY },
-  { 0 }
+  { nullptr,  0 }
 };
 
 bool
 nsGenericHTMLElement::ParseTableHAlignValue(const nsAString& aString,
                                             nsAttrValue& aResult)
 {
   return aResult.ParseEnumValue(aString, kTableHAlignTable, false);
 }
@@ -1137,17 +1137,17 @@ nsGenericHTMLElement::ParseTableHAlignVa
 static const nsAttrValue::EnumTable kTableCellHAlignTable[] = {
   { "left",   NS_STYLE_TEXT_ALIGN_MOZ_LEFT },
   { "right",  NS_STYLE_TEXT_ALIGN_MOZ_RIGHT },
   { "center", NS_STYLE_TEXT_ALIGN_MOZ_CENTER },
   { "char",   NS_STYLE_TEXT_ALIGN_CHAR },
   { "justify",NS_STYLE_TEXT_ALIGN_JUSTIFY },
   { "middle", NS_STYLE_TEXT_ALIGN_MOZ_CENTER },
   { "absmiddle", NS_STYLE_TEXT_ALIGN_CENTER },
-  { 0 }
+  { nullptr,  0 }
 };
 
 bool
 nsGenericHTMLElement::ParseTableCellHAlignValue(const nsAString& aString,
                                                 nsAttrValue& aResult)
 {
   return aResult.ParseEnumValue(aString, kTableCellHAlignTable, false);
 }
@@ -1185,22 +1185,22 @@ nsGenericHTMLElement::ParseImageAttribut
   return false;
 }
 
 bool
 nsGenericHTMLElement::ParseReferrerAttribute(const nsAString& aString,
                                              nsAttrValue& aResult)
 {
   static const nsAttrValue::EnumTable kReferrerTable[] = {
-    { net::kRPS_No_Referrer, net::RP_No_Referrer },
-    { net::kRPS_Origin, net::RP_Origin },
-    { net::kRPS_Origin_When_Cross_Origin, net::RP_Origin_When_Crossorigin },
-    { net::kRPS_No_Referrer_When_Downgrade, net::RP_No_Referrer_When_Downgrade },
-    { net::kRPS_Unsafe_URL, net::RP_Unsafe_URL },
-    { 0 }
+    { net::kRPS_No_Referrer, static_cast<int16_t>(net::RP_No_Referrer) },
+    { net::kRPS_Origin, static_cast<int16_t>(net::RP_Origin) },
+    { net::kRPS_Origin_When_Cross_Origin, static_cast<int16_t>(net::RP_Origin_When_Crossorigin) },
+    { net::kRPS_No_Referrer_When_Downgrade, static_cast<int16_t>(net::RP_No_Referrer_When_Downgrade) },
+    { net::kRPS_Unsafe_URL, static_cast<int16_t>(net::RP_Unsafe_URL) },
+    { nullptr, 0 }
   };
   return aResult.ParseEnumValue(aString, kReferrerTable, false);
 }
 
 bool
 nsGenericHTMLElement::ParseFrameborderValue(const nsAString& aString,
                                             nsAttrValue& aResult)
 {
--- a/dom/html/nsIFormControl.h
+++ b/dom/html/nsIFormControl.h
@@ -33,24 +33,24 @@ enum FormControlsTypes {
 
   // Elements with different types, the value is used as a mask.
   // When changing the order, adding or removing elements, be sure to update
   // the static_assert checks accordingly.
   NS_FORM_BUTTON_ELEMENT = 0x40, // 0b01000000
   NS_FORM_INPUT_ELEMENT  = 0x80  // 0b10000000
 };
 
-enum ButtonElementTypes {
+enum ButtonElementTypes : uint8_t {
   NS_FORM_BUTTON_BUTTON = NS_FORM_BUTTON_ELEMENT + 1,
   NS_FORM_BUTTON_RESET,
   NS_FORM_BUTTON_SUBMIT,
   eButtonElementTypesMax
 };
 
-enum InputElementTypes {
+enum InputElementTypes : uint8_t {
   NS_FORM_INPUT_BUTTON = NS_FORM_INPUT_ELEMENT + 1,
   NS_FORM_INPUT_CHECKBOX,
   NS_FORM_INPUT_COLOR,
   NS_FORM_INPUT_DATE,
   NS_FORM_INPUT_EMAIL,
   NS_FORM_INPUT_FILE,
   NS_FORM_INPUT_HIDDEN,
   NS_FORM_INPUT_RESET,
--- a/dom/ipc/TabContext.cpp
+++ b/dom/ipc/TabContext.cpp
@@ -297,18 +297,18 @@ MaybeInvalidTabContext::MaybeInvalidTabC
 {
   bool isMozBrowserElement = false;
   bool isPrerendered = false;
   uint32_t containingAppId = NO_APP_ID;
   DocShellOriginAttributes originAttributes;
   nsAutoCString originSuffix;
   nsAutoCString signedPkgOriginNoSuffix;
   nsAutoString presentationURL;
-  UIStateChangeType showAccelerators;
-  UIStateChangeType showFocusRings;
+  UIStateChangeType showAccelerators = UIStateChangeType_NoChange;
+  UIStateChangeType showFocusRings = UIStateChangeType_NoChange;
 
   switch(aParams.type()) {
     case IPCTabContext::TPopupIPCTabContext: {
       const PopupIPCTabContext &ipcContext = aParams.get_PopupIPCTabContext();
 
       TabContext *context;
       if (ipcContext.opener().type() == PBrowserOrId::TPBrowserParent) {
         context = TabParent::GetFrom(ipcContext.opener().get_PBrowserParent());
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -1001,17 +1001,17 @@ MediaRecorder::MediaRecorder(AudioNode& 
   // union stream in recorder session won't be able to copy data from the
   // stream of non-destination node. Create a pipe stream in this case.
   if (aSrcAudioNode.NumberOfOutputs() > 0) {
     AudioContext* ctx = aSrcAudioNode.Context();
     AudioNodeEngine* engine = new AudioNodeEngine(nullptr);
     AudioNodeStream::Flags flags =
       AudioNodeStream::EXTERNAL_OUTPUT |
       AudioNodeStream::NEED_MAIN_THREAD_FINISHED;
-    mPipeStream = AudioNodeStream::Create(ctx, engine, flags);
+    mPipeStream = AudioNodeStream::Create(ctx, engine, flags, ctx->Graph());
     AudioNodeStream* ns = aSrcAudioNode.GetStream();
     if (ns) {
       mInputPort =
         mPipeStream->AllocateInputPort(aSrcAudioNode.GetStream(),
                                        TRACK_ANY, TRACK_ANY,
                                        0, aSrcOutput);
     }
   }
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -144,17 +144,17 @@ public:
       MOZ_ASSERT(ok);
 
       int32_t size;
       ok |= NS_SUCCEEDED(aInfo->Size(&size));
       MOZ_ASSERT(ok);
 
       NS_ENSURE_TRUE_VOID(ok);
 
-      if (size > 0) {
+      if (size > 0 && durationUs.value() > 0) {
         RefPtr<layers::Image> img =
           new SurfaceTextureImage(mDecoder->mSurfaceTexture.get(), mDecoder->mConfig.mDisplay,
                                   gl::OriginPos::BottomLeft);
 
         RefPtr<VideoData> v =
           VideoData::CreateFromImage(mDecoder->mConfig,
                                     offset,
                                     presentationTimeUs,
@@ -166,17 +166,16 @@ public:
                                                   mDecoder->mConfig.mDisplay.width,
                                                   mDecoder->mConfig.mDisplay.height));
 
         mDecoderCallback->Output(v);
       }
 
       if ((flags & MediaCodec::BUFFER_FLAG_END_OF_STREAM) != 0) {
         mDecoderCallback->DrainComplete();
-        return;
       }
     }
 
     friend class RemoteDataDecoder;
 
   private:
     RemoteVideoDecoder* mDecoder;
   };
@@ -218,16 +217,25 @@ public:
   }
 
   nsresult Flush() override
   {
     mInputDurations.Clear();
     return RemoteDataDecoder::Flush();
   }
 
+  nsresult Drain() override
+  {
+    nsresult res = RemoteDataDecoder::Drain();
+    NS_ENSURE_SUCCESS(res, res);
+
+    mInputDurations.Put(0);
+    return NS_OK;
+  }
+
   nsresult Input(MediaRawData* aSample) override
   {
     nsresult res = RemoteDataDecoder::Input(aSample);
     NS_ENSURE_SUCCESS(res, res);
 
     mInputDurations.Put(aSample->mDuration);
     return NS_OK;
   }
@@ -435,17 +443,17 @@ nsresult
 RemoteDataDecoder::Drain()
 {
   BufferInfo::LocalRef bufferInfo;
   nsresult rv = BufferInfo::New(&bufferInfo);
   NS_ENSURE_SUCCESS(rv, rv);
   bufferInfo->Set(0, 0, -1, MediaCodec::BUFFER_FLAG_END_OF_STREAM);
 
   mJavaDecoder->Input(nullptr, bufferInfo);
-  return NS_ERROR_FAILURE;
+  return NS_OK;
 }
 
 nsresult
 RemoteDataDecoder::Shutdown()
 {
   LOG("");
   MOZ_ASSERT(mJavaDecoder && mJavaCallbacks);
 
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -86,16 +86,17 @@ WMFVideoMFTManager::WMFVideoMFTManager(
   , mVideoStride(0)
   , mImageSize(aConfig.mImage)
   , mImageContainer(aImageContainer)
   , mDXVAEnabled(aDXVAEnabled)
   , mLayersBackend(aLayersBackend)
   , mNullOutputCount(0)
   , mGotValidOutputAfterNullOutput(false)
   , mGotExcessiveNullOutput(false)
+  , mIsValid(true)
   // mVideoStride, mVideoWidth, mVideoHeight, mUseHwAccel are initialized in
   // Init().
 {
   MOZ_COUNT_CTOR(WMFVideoMFTManager);
 
   // Need additional checks/params to check vp8/vp9
   if (MP4Decoder::IsH264(aConfig.mMimeType)) {
     mStreamType = H264;
@@ -366,18 +367,42 @@ WMFVideoMFTManager::InitializeDXVA(bool 
     NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
   }
   mDXVA2Manager = event->mDXVA2Manager;
 
   return mDXVA2Manager != nullptr;
 }
 
 bool
+WMFVideoMFTManager::ValidateVideoInfo()
+{
+  // The WMF H.264 decoder is documented to have a minimum resolution
+  // 48x48 pixels. We've observed the decoder working for output smaller than
+  // that, but on some output it hangs in IMFTransform::ProcessOutput(), so
+  // we just reject streams which are less than the documented minimum.
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/dd797815(v=vs.85).aspx
+  static const int32_t MIN_H264_FRAME_DIMENSION = 48;
+  if (mStreamType == H264 &&
+      (mVideoInfo.mImage.width < MIN_H264_FRAME_DIMENSION ||
+       mVideoInfo.mImage.height < MIN_H264_FRAME_DIMENSION)) {
+    LogToBrowserConsole(NS_LITERAL_STRING(
+      "Can't decode H.264 stream with width or height less than 48 pixels."));
+    mIsValid = false;
+  }
+
+  return mIsValid;
+}
+
+bool
 WMFVideoMFTManager::Init()
 {
+  if (!ValidateVideoInfo()) {
+    return false;
+  }
+
   bool success = InitInternal(/* aForceD3D9 = */ false);
 
   if (success && mDXVA2Manager) {
     // If we had some failures but eventually made it work,
     // make sure we preserve the messages.
     if (mDXVA2Manager->IsD3D11()) {
       mDXVAFailureReason.Append(NS_LITERAL_CSTRING("Using D3D11 API"));
     } else {
@@ -479,16 +504,20 @@ WMFVideoMFTManager::SetDecoderMediaTypes
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   return mDecoder->SetMediaTypes(inputType, outputType);
 }
 
 HRESULT
 WMFVideoMFTManager::Input(MediaRawData* aSample)
 {
+  if (!mIsValid) {
+    return E_FAIL;
+  }
+
   if (!mDecoder) {
     // This can happen during shutdown.
     return E_FAIL;
   }
 
   HRESULT hr = mDecoder->CreateInputSample(aSample->Data(),
                                            uint32_t(aSample->Size()),
                                            aSample->mTime,
@@ -901,11 +930,12 @@ WMFVideoMFTManager::IsHardwareAccelerate
 }
 
 void
 WMFVideoMFTManager::ConfigurationChanged(const TrackInfo& aConfig)
 {
   MOZ_ASSERT(aConfig.GetAsVideoInfo());
   mVideoInfo = *aConfig.GetAsVideoInfo();
   mImageSize = mVideoInfo.mImage;
+  ValidateVideoInfo();
 }
 
 } // namespace mozilla
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.h
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.h
@@ -46,16 +46,18 @@ public:
   {
     nsCString failureReason;
     return IsHardwareAccelerated(failureReason)
       ? "wmf hardware video decoder" : "wmf software video decoder";
   }
 
 private:
 
+  bool ValidateVideoInfo();
+
   bool InitializeDXVA(bool aForceD3D9);
 
   bool InitInternal(bool aForceD3D9);
 
   HRESULT ConfigureVideoFrameGeometry();
 
   HRESULT CreateBasicVideoFrame(IMFSample* aSample,
                                 int64_t aStreamOffset,
@@ -96,13 +98,14 @@ private:
   StreamType mStreamType;
 
   const GUID& GetMFTGUID();
   const GUID& GetMediaSubtypeGUID();
 
   uint32_t mNullOutputCount;
   bool mGotValidOutputAfterNullOutput;
   bool mGotExcessiveNullOutput;
+  bool mIsValid;
 };
 
 } // namespace mozilla
 
 #endif // WMFVideoMFTManager_h_
--- a/dom/media/test/manifest.js
+++ b/dom/media/test/manifest.js
@@ -528,16 +528,30 @@ var gErrorTests = [
   { name:"bug504843.ogv", type:"video/ogg" },
   { name:"bug501279.ogg", type:"audio/ogg" },
   { name:"bug580982.webm", type:"video/webm" },
   { name:"bug603918.webm", type:"video/webm" },
   { name:"bug604067.webm", type:"video/webm" },
   { name:"bogus.duh", type:"bogus/duh" }
 ];
 
+function IsWindowsVistaOrLater() {
+  var re = /Windows NT (\d+.\d)/;
+  var winver = manifestNavigator().userAgent.match(re);
+  return winver && winver.length == 2 && parseFloat(winver[1]) >= 6.0;
+}
+
+// Windows' H.264 decoder cannot handle H.264 streams with resolution
+// less than 48x48 pixels. We refuse to play and error on such streams.
+if (IsWindowsVistaOrLater() &&
+    manifestVideo().canPlayType('video/mp4; codecs="avc1.42E01E"')) {
+  gErrorTests = gErrorTests.concat({name: "red-46x48.mp4", type:"video/mp4"},
+                                   {name: "red-48x46.mp4", type:"video/mp4"});
+}
+
 // These are files that have nontrivial duration and are useful for seeking within.
 var gSeekTests = [
   { name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
   { name:"audio.wav", type:"audio/x-wav", duration:0.031247 },
   { name:"seek.ogv", type:"video/ogg", duration:3.966 },
   { name:"320x240.ogv", type:"video/ogg", duration:0.266 },
   { name:"seek.webm", type:"video/webm", duration:3.966 },
   { name:"sine.webm", type:"audio/webm", duration:4.001 },
@@ -557,22 +571,16 @@ var gFastSeekTests = [
   // Note: Not all keyframes in the file are actually referenced in the Cues in this file.
   { name:"seek.webm", type:"video/webm", keyframes:[0, 0.8, 1.6, 2.4, 3.2]},
   // Note: the sync points are the points on both the audio and video streams
   // before the keyframes. You can't just assume that the keyframes are the sync
   // points, as the audio required for that sync point may be before the keyframe.
   { name:"bug516323.indexed.ogv", type:"video/ogg", keyframes:[0, 0.46, 3.06] },
 ];
 
-function IsWindows8OrLater() {
-  var re = /Windows NT (\d.\d)/;
-  var winver = manifestNavigator().userAgent.match(re);
-  return winver && winver.length == 2 && parseFloat(winver[1]) >= 6.2;
-}
-
 // These files are WebMs without cues. They're seekable within their buffered
 // ranges. If work renders WebMs fully seekable these files should be moved
 // into gSeekTests
 var gCuelessWebMTests = [
   { name:"no-cues.webm", type:"video/webm", duration:3.967 },
 ];
 
 // These are files that are non seekable, due to problems with the media,
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -486,16 +486,20 @@ support-files =
   r11025_s16_c1_trailing.wav^headers^
   r11025_u8_c1.wav
   r11025_u8_c1.wav^headers^
   r11025_u8_c1_trunc.wav
   r11025_u8_c1_trunc.wav^headers^
   r16000_u8_c1_list.wav
   r16000_u8_c1_list.wav^headers^
   reactivate_helper.html
+  red-46x48.mp4
+  red-46x48.mp4^headers^
+  red-48x46.mp4
+  red-48x46.mp4^headers^
   redirect.sjs
   referer.sjs
   region.vtt
   resolution-change.webm
   resolution-change.webm^headers^
   sample.3gp
   sample.3g2
   sample-fisbone-skeleton4.ogv
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0760cc1c165dc13a50cf0e8b85f75ac7efb5c6f7
GIT binary patch
literal 1548
zc$|GyL5mzk6t0<&U?3pEf<ZP=;(8EwX1jZ4C%Xd;ktH|>BVNowgtoe>db*jau9~Wv
z*_pklASwj$wm-m|;3)`ta1Vk9!9U=NpgAZ;qsc{t9GtxB+0D$ZCPi1juj+m8y?XCe
zH$n(!ZmJ8bBtmuMCCrG7X()9&L<l{R8O}lXk7UGw{Oa7<+3d?7uiifR^|1HF{`&X$
z@VUS4PNGg5H!xL(<FLJjP1I>^;me!b+ff*RqSpk%#<kb3zT9Zzm#*&slW_`)9i^v&
zXAVa}&}u|MxCxuNbGo~+vAet592bl$LCU5w{S6<bnL8<fqqHkjX}g$`l+ZZDhG%gL
zGoA`X2XWX9x&bC75mU?KVAARalQ3*y$>W?)upOm<E@N${U<&WVusCc6U<5BLiwS3m
z_ZWgjVo2HNaoE8$H%gKOOhfDp7oxC$wkBJQIskMi;{c98*q$nR9EIy)h%;hcqV1s2
zo-ubY)QQS6%U#@vu*(ftct=910m)&JEcUD@C|_&@xHMG^T1evjIf~M8BM6XSKNTZG
zrU_M269;PwC31!oCAa{hA)b>NBDsZPrHP&bE?67^CX8sGMVh1q@sU_zc$L>KFZ#Jl
z0V&PPq^~q+=bIW#2Yd>?<7hKDf{83je{{-APDd_o2e=CED{8pS!P?N|VYXgxXiQRr
zWeR2WS)~Q=AwRel*aN48?++gO)Z(Do0jPbLN|H(3*#=-8*Kr#c8d?U|11!Kb8NzMw
zy+!Z9eSVRD_#S#@_V3>}e~P|4|HCgghBv;xxAzwNp!Mq7+qYIfx&8Z{Z+||he{}Bi
zcMssY|0$IkgPD-yoS_rr%x~4KA^$8!?Ii2;`U-mNsKfI#q&@)rrW;f-&-~Y$kNx;K
z{~NanGpLqpoH+|}l?&?@7Ds+7+uFyH6$Aj5>~!u|s`>gi)uNFzVU9KagI#7`_aAVN
zluU5Xsdb@g8Gx0steh?%bB)b)s|+s6QW_UVM&KiS-?~(Q+_%oMCC>Z)pY@1R_{0O$
z1KE2d<Mg;Vo54E(uRAq{F3fJBi+zxv9bUVC?<wCCUoBLBa^;2n8akW*ap&f`x3aD(
z>v4?QQQ=qNwDjcu!?^;wy}7D>cd<v+65ls19Wm$I@rLA;^yAs((nGrp(ON90(J1Eq
ceg0FYyu(ZI7bnLBc!i9!%?^Ac%`gZb08m<M@&Et;
new file mode 100644
--- /dev/null
+++ b/dom/media/test/red-46x48.mp4^headers^
@@ -0,0 +1,1 @@
+Cache-Control: no-store
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d83de4027d11f07f6e76c02f797a540ab9f0eb1b
GIT binary patch
literal 1548
zc$|GyL5mzk6t0<&pb!vY!5}M?xE{ov+3ud%$?iZyWC_l}h!=AZp{=f}o-U@UtEReU
zc4jXshzdcx?GNxKc#t6I!955b1pk05g65zYjV6~Mc*x{c&u(URH7UCKeO2#!@6~&+
zx)DM+b5kQqtq`gsFJVSxOhaYbAwuY}%yJI8e-tAQ<k#oU&Sqczbou7NZ->1v_t$^G
zhtK_edlGfpxPhs*9Ea^KY@<$V3t!ya-j2cm6ul-8Hm<yO`Q=6%zjSp6n2b|U>}WHU
zJaae-f>t96!cExBoip8yjosbd=2$SUB`KQP_BVW#X6}>(jy6u{qU>TyQbOYpTb{)&
z%y=p_9mHWb=mwY+L{3W{2a{Gen1o>qD<0>3g3D0~=pr`d6infrSQdxP0F2;;m6&jr
zc#k1iB$gC?9)}%FbE_3ez%<0paw$a#Xlt^?r~^QUDh}WXgzf2q$5FT*hBzaoOH4Ts
z#xv#)h9=QjR&p0NBJ6St7T%H6dO&hmB#XUL3d$E70WNG6g9=5QKZhtBw~_z}_ER~s
zWSUT|3~{igP$FlED8K~}E%BVp62(g>R+^Y8V8P-DFk!^_EYc(u#7APn@+z-gF8aAk
z0cpdFq^}KV=bHvh2Yd>?<7hKDf{7A^KRV?Frz01)16&376}7y~!P?T~LAG9RX-ra!
zRSISGS)~H_kRMzN?19tL_XiJsYH`r)05m>KCCMc2Yy&7AH*p&a11*E=0T$qz4B<BT
z-lBKlKEKF6d=EW6yLb2c&(Zgf|M=^*;k9q>?7hW4XuZ1j_Knp~ZvJuWyI&6KAD#Q+
z-2=Gpe<`iUU?$Z#XXwN@^LsUG$UlowJIOk|zJwk*>hL@TsSg0Z=>}EIGynDGV?R31
z|HchqHml_tXRZXf%4O*m7Ds+7+uFy92?78sb~^V<)qMTiYSGA<w8t9%!7eke`VY8A
z3MRSd)Vk2L48Y1*RZf?WxyI(YRR$MjDUI_ZEAbKjw{)olxnH`HEpguW|E!0U!YAHD
zJ&?VJGER>RvzfO8uRAq{&d+Y33w@BE9bUQj`IEjUzFMgM#L5f%HFP%r^VaosZ&kXg
ztj95GM}=R7)6$ds59SK!_U5Yk?ZqC|OMKtBbi|x*#~X@Q(vN4COAqa`#2C4pMnlZ|
c`~0U)dxy`!Uz{8l;1x2?HaqZ*G{Ydg4<15mwg3PC
new file mode 100644
--- /dev/null
+++ b/dom/media/test/red-48x46.mp4^headers^
@@ -0,0 +1,1 @@
+Cache-Control: no-store
--- a/dom/media/webaudio/AnalyserNode.cpp
+++ b/dom/media/webaudio/AnalyserNode.cpp
@@ -108,17 +108,18 @@ AnalyserNode::AnalyserNode(AudioContext*
               ChannelInterpretation::Speakers)
   , mAnalysisBlock(2048)
   , mMinDecibels(-100.)
   , mMaxDecibels(-30.)
   , mSmoothingTimeConstant(.8)
 {
   mStream = AudioNodeStream::Create(aContext,
                                     new AnalyserNodeEngine(this),
-                                    AudioNodeStream::NO_STREAM_FLAGS);
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    aContext->Graph());
 
   // Enough chunks must be recorded to handle the case of fftSize being
   // increased to maximum immediately before getFloatTimeDomainData() is
   // called, for example.
   Unused << mChunks.SetLength(CHUNK_COUNT, fallible);
 
   AllocateBuffer();
 }
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -597,17 +597,18 @@ AudioBufferSourceNode::AudioBufferSource
   // mOffset and mDuration are initialized in Start().
   , mPlaybackRate(new AudioParam(this, PLAYBACKRATE, 1.0f, "playbackRate"))
   , mDetune(new AudioParam(this, DETUNE, 0.0f, "detune"))
   , mLoop(false)
   , mStartCalled(false)
 {
   AudioBufferSourceNodeEngine* engine = new AudioBufferSourceNodeEngine(this, aContext->Destination());
   mStream = AudioNodeStream::Create(aContext, engine,
-                                    AudioNodeStream::NEED_MAIN_THREAD_FINISHED);
+                                    AudioNodeStream::NEED_MAIN_THREAD_FINISHED,
+                                    aContext->Graph());
   engine->SetSourceStream(mStream);
   mStream->AddMainThreadListener(this);
 }
 
 AudioBufferSourceNode::~AudioBufferSourceNode()
 {
 }
 
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -66,30 +66,30 @@ AudioNodeStream::DestroyImpl()
   ProcessedMediaStream::DestroyImpl();
 }
 
 /* static */ already_AddRefed<AudioNodeStream>
 AudioNodeStream::Create(AudioContext* aCtx, AudioNodeEngine* aEngine,
                         Flags aFlags, MediaStreamGraph* aGraph)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_RELEASE_ASSERT(aGraph);
 
   // MediaRecorders use an AudioNodeStream, but no AudioNode
   AudioNode* node = aEngine->NodeMainThread();
-  MediaStreamGraph* graph = aGraph ? aGraph : aCtx->Graph();
 
   RefPtr<AudioNodeStream> stream =
-    new AudioNodeStream(aEngine, aFlags, graph->GraphRate());
+    new AudioNodeStream(aEngine, aFlags, aGraph->GraphRate());
   stream->mSuspendedCount += aCtx->ShouldSuspendNewStream();
   if (node) {
     stream->SetChannelMixingParametersImpl(node->ChannelCount(),
                                            node->ChannelCountModeValue(),
                                            node->ChannelInterpretationValue());
   }
-  graph->AddStream(stream);
+  aGraph->AddStream(stream);
   return stream.forget();
 }
 
 size_t
 AudioNodeStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = 0;
 
--- a/dom/media/webaudio/AudioNodeStream.h
+++ b/dom/media/webaudio/AudioNodeStream.h
@@ -57,23 +57,22 @@ public:
     // Internal AudioNodeStreams can only pass their output to another
     // AudioNode, whereas external AudioNodeStreams can pass their output
     // to other ProcessedMediaStreams or hardware audio output.
     EXTERNAL_OUTPUT = 1U << 2,
   };
   /**
    * Create a stream that will process audio for an AudioNode.
    * Takes ownership of aEngine.
-   * If aGraph is non-null, use that as the MediaStreamGraph, otherwise use
-   * aCtx's graph. aGraph is only non-null when called for AudioDestinationNode
-   * since the context's graph hasn't been set up in that case.
+   * aGraph is required and equals the graph of aCtx in most cases. An exception
+   * is AudioDestinationNode where the context's graph hasn't been set up yet.
    */
   static already_AddRefed<AudioNodeStream>
   Create(AudioContext* aCtx, AudioNodeEngine* aEngine, Flags aKind,
-         MediaStreamGraph* aGraph = nullptr);
+         MediaStreamGraph* aGraph);
 
 protected:
   /**
    * Transfers ownership of aEngine to the new AudioNodeStream.
    */
   AudioNodeStream(AudioNodeEngine* aEngine,
                   Flags aFlags,
                   TrackRate aSampleRate);
--- a/dom/media/webaudio/AudioParam.cpp
+++ b/dom/media/webaudio/AudioParam.cpp
@@ -86,17 +86,18 @@ AudioParam::Stream()
 {
   if (mStream) {
     return mStream;
   }
 
   AudioNodeEngine* engine = new AudioNodeEngine(nullptr);
   RefPtr<AudioNodeStream> stream =
     AudioNodeStream::Create(mNode->Context(), engine,
-                            AudioNodeStream::NO_STREAM_FLAGS);
+                            AudioNodeStream::NO_STREAM_FLAGS,
+                            mNode->Context()->Graph());
 
   // Force the input to have only one channel, and make it down-mix using
   // the speaker rules if needed.
   stream->SetChannelMixingParametersImpl(1, ChannelCountMode::Explicit, ChannelInterpretation::Speakers);
   // Mark as an AudioParam helper stream
   stream->SetAudioParamHelperStream();
 
   mStream = stream.forget();
--- a/dom/media/webaudio/BiquadFilterNode.cpp
+++ b/dom/media/webaudio/BiquadFilterNode.cpp
@@ -254,17 +254,18 @@ BiquadFilterNode::BiquadFilterNode(Audio
                               350.f, "frequency"))
   , mDetune(new AudioParam(this, BiquadFilterNodeEngine::DETUNE, 0.f, "detune"))
   , mQ(new AudioParam(this, BiquadFilterNodeEngine::Q, 1.f, "Q"))
   , mGain(new AudioParam(this, BiquadFilterNodeEngine::GAIN, 0.f, "gain"))
 {
   uint64_t windowID = aContext->GetParentObject()->WindowID();
   BiquadFilterNodeEngine* engine = new BiquadFilterNodeEngine(this, aContext->Destination(), windowID);
   mStream = AudioNodeStream::Create(aContext, engine,
-                                    AudioNodeStream::NO_STREAM_FLAGS);
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    aContext->Graph());
 }
 
 BiquadFilterNode::~BiquadFilterNode()
 {
 }
 
 size_t
 BiquadFilterNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
--- a/dom/media/webaudio/ChannelMergerNode.cpp
+++ b/dom/media/webaudio/ChannelMergerNode.cpp
@@ -66,17 +66,18 @@ ChannelMergerNode::ChannelMergerNode(Aud
   : AudioNode(aContext,
               1,
               ChannelCountMode::Explicit,
               ChannelInterpretation::Speakers)
   , mInputCount(aInputCount)
 {
   mStream = AudioNodeStream::Create(aContext,
                                     new ChannelMergerNodeEngine(this),
-                                    AudioNodeStream::NO_STREAM_FLAGS);
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    aContext->Graph());
 }
 
 ChannelMergerNode::~ChannelMergerNode()
 {
 }
 
 JSObject*
 ChannelMergerNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
--- a/dom/media/webaudio/ChannelSplitterNode.cpp
+++ b/dom/media/webaudio/ChannelSplitterNode.cpp
@@ -57,17 +57,18 @@ ChannelSplitterNode::ChannelSplitterNode
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
   , mOutputCount(aOutputCount)
 {
   mStream = AudioNodeStream::Create(aContext,
                                     new ChannelSplitterNodeEngine(this),
-                                    AudioNodeStream::NO_STREAM_FLAGS);
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    aContext->Graph());
 }
 
 ChannelSplitterNode::~ChannelSplitterNode()
 {
 }
 
 JSObject*
 ChannelSplitterNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
--- a/dom/media/webaudio/ConvolverNode.cpp
+++ b/dom/media/webaudio/ConvolverNode.cpp
@@ -195,17 +195,18 @@ ConvolverNode::ConvolverNode(AudioContex
   : AudioNode(aContext,
               2,
               ChannelCountMode::Clamped_max,
               ChannelInterpretation::Speakers)
   , mNormalize(true)
 {
   ConvolverNodeEngine* engine = new ConvolverNodeEngine(this, mNormalize);
   mStream = AudioNodeStream::Create(aContext, engine,
-                                    AudioNodeStream::NO_STREAM_FLAGS);
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    aContext->Graph());
 }
 
 ConvolverNode::~ConvolverNode()
 {
 }
 
 size_t
 ConvolverNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
--- a/dom/media/webaudio/DelayNode.cpp
+++ b/dom/media/webaudio/DelayNode.cpp
@@ -197,17 +197,18 @@ DelayNode::DelayNode(AudioContext* aCont
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
   , mDelay(new AudioParam(this, DelayNodeEngine::DELAY, 0.0f, "delayTime"))
 {
   DelayNodeEngine* engine =
     new DelayNodeEngine(this, aContext->Destination(),
                         aContext->SampleRate() * aMaxDelay);
   mStream = AudioNodeStream::Create(aContext, engine,
-                                    AudioNodeStream::NO_STREAM_FLAGS);
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    aContext->Graph());
 }
 
 DelayNode::~DelayNode()
 {
 }
 
 size_t
 DelayNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
--- a/dom/media/webaudio/DynamicsCompressorNode.cpp
+++ b/dom/media/webaudio/DynamicsCompressorNode.cpp
@@ -196,17 +196,18 @@ DynamicsCompressorNode::DynamicsCompress
   , mReduction(0)
   , mAttack(new AudioParam(this, DynamicsCompressorNodeEngine::ATTACK,
                            0.003f, "attack"))
   , mRelease(new AudioParam(this, DynamicsCompressorNodeEngine::RELEASE,
                             0.25f, "release"))
 {
   DynamicsCompressorNodeEngine* engine = new DynamicsCompressorNodeEngine(this, aContext->Destination());
   mStream = AudioNodeStream::Create(aContext, engine,
-                                    AudioNodeStream::NO_STREAM_FLAGS);
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    aContext->Graph());
 }
 
 DynamicsCompressorNode::~DynamicsCompressorNode()
 {
 }
 
 size_t
 DynamicsCompressorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
--- a/dom/media/webaudio/GainNode.cpp
+++ b/dom/media/webaudio/GainNode.cpp
@@ -119,17 +119,18 @@ GainNode::GainNode(AudioContext* aContex
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
   , mGain(new AudioParam(this, GainNodeEngine::GAIN, 1.0f, "gain"))
 {
   GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination());
   mStream = AudioNodeStream::Create(aContext, engine,
-                                    AudioNodeStream::NO_STREAM_FLAGS);
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    aContext->Graph());
 }
 
 GainNode::~GainNode()
 {
 }
 
 size_t
 GainNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
--- a/dom/media/webaudio/IIRFilterNode.cpp
+++ b/dom/media/webaudio/IIRFilterNode.cpp
@@ -159,17 +159,18 @@ IIRFilterNode::IIRFilterNode(AudioContex
   }
 
   // We check that this is exactly equal to one later in blink/IIRFilter.cpp
   elements[0] = 1.0;
 
   uint64_t windowID = aContext->GetParentObject()->WindowID();
   IIRFilterNodeEngine* engine = new IIRFilterNodeEngine(this, aContext->Destination(), mFeedforward, mFeedback, windowID);
   mStream = AudioNodeStream::Create(aContext, engine,
-                                    AudioNodeStream::NO_STREAM_FLAGS);
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    aContext->Graph());
 }
 
 IIRFilterNode::~IIRFilterNode()
 {
 }
 
 size_t
 IIRFilterNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
--- a/dom/media/webaudio/MediaStreamAudioDestinationNode.cpp
+++ b/dom/media/webaudio/MediaStreamAudioDestinationNode.cpp
@@ -44,17 +44,18 @@ MediaStreamAudioDestinationNode::MediaSt
                                MediaSegment::AUDIO, source,
                                MediaTrackConstraints());
   mDOMStream->AddTrackInternal(track);
 
   ProcessedMediaStream* outputStream = mDOMStream->GetInputStream()->AsProcessedStream();
   MOZ_ASSERT(!!outputStream);
   AudioNodeEngine* engine = new AudioNodeEngine(this);
   mStream = AudioNodeStream::Create(aContext, engine,
-                                    AudioNodeStream::EXTERNAL_OUTPUT);
+                                    AudioNodeStream::EXTERNAL_OUTPUT,
+                                    aContext->Graph());
   mPort = outputStream->AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK);
 }
 
 MediaStreamAudioDestinationNode::~MediaStreamAudioDestinationNode()
 {
 }
 
 size_t
--- a/dom/media/webaudio/OscillatorNode.cpp
+++ b/dom/media/webaudio/OscillatorNode.cpp
@@ -415,17 +415,18 @@ OscillatorNode::OscillatorNode(AudioCont
   , mType(OscillatorType::Sine)
   , mFrequency(new AudioParam(this, OscillatorNodeEngine::FREQUENCY,
                               440.0f, "frequency"))
   , mDetune(new AudioParam(this, OscillatorNodeEngine::DETUNE, 0.0f, "detune"))
   , mStartCalled(false)
 {
   OscillatorNodeEngine* engine = new OscillatorNodeEngine(this, aContext->Destination());
   mStream = AudioNodeStream::Create(aContext, engine,
-                                    AudioNodeStream::NEED_MAIN_THREAD_FINISHED);
+                                    AudioNodeStream::NEED_MAIN_THREAD_FINISHED,
+                                    aContext->Graph());
   engine->SetSourceStream(mStream);
   mStream->AddMainThreadListener(this);
 }
 
 OscillatorNode::~OscillatorNode()
 {
 }
 
--- a/dom/media/webaudio/PannerNode.cpp
+++ b/dom/media/webaudio/PannerNode.cpp
@@ -310,17 +310,18 @@ PannerNode::PannerNode(AudioContext* aCo
   , mMaxDistance(10000.)
   , mRolloffFactor(1.)
   , mConeInnerAngle(360.)
   , mConeOuterAngle(360.)
   , mConeOuterGain(0.)
 {
   mStream = AudioNodeStream::Create(aContext,
                                     new PannerNodeEngine(this, aContext->Destination()),
-                                    AudioNodeStream::NO_STREAM_FLAGS);
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    aContext->Graph());
   // We should register once we have set up our stream and engine.
   Context()->Listener()->RegisterPannerNode(this);
 }
 
 PannerNode::~PannerNode()
 {
   if (Context()) {
     Context()->UnregisterPannerNode(this);
--- a/dom/media/webaudio/ScriptProcessorNode.cpp
+++ b/dom/media/webaudio/ScriptProcessorNode.cpp
@@ -499,17 +499,18 @@ ScriptProcessorNode::ScriptProcessorNode
 {
   MOZ_ASSERT(BufferSize() % WEBAUDIO_BLOCK_SIZE == 0, "Invalid buffer size");
   ScriptProcessorNodeEngine* engine =
     new ScriptProcessorNodeEngine(this,
                                   aContext->Destination(),
                                   BufferSize(),
                                   aNumberOfInputChannels);
   mStream = AudioNodeStream::Create(aContext, engine,
-                                    AudioNodeStream::NO_STREAM_FLAGS);
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    aContext->Graph());
 }
 
 ScriptProcessorNode::~ScriptProcessorNode()
 {
 }
 
 size_t
 ScriptProcessorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
--- a/dom/media/webaudio/StereoPannerNode.cpp
+++ b/dom/media/webaudio/StereoPannerNode.cpp
@@ -174,17 +174,18 @@ StereoPannerNode::StereoPannerNode(Audio
   : AudioNode(aContext,
               2,
               ChannelCountMode::Clamped_max,
               ChannelInterpretation::Speakers)
   , mPan(new AudioParam(this, StereoPannerNodeEngine::PAN, 0.f, "pan"))
 {
   StereoPannerNodeEngine* engine = new StereoPannerNodeEngine(this, aContext->Destination());
   mStream = AudioNodeStream::Create(aContext, engine,
-                                    AudioNodeStream::NO_STREAM_FLAGS);
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    aContext->Graph());
 }
 
 StereoPannerNode::~StereoPannerNode()
 {
 }
 
 size_t
 StereoPannerNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
--- a/dom/media/webaudio/WaveShaperNode.cpp
+++ b/dom/media/webaudio/WaveShaperNode.cpp
@@ -317,17 +317,18 @@ WaveShaperNode::WaveShaperNode(AudioCont
               ChannelInterpretation::Speakers)
   , mCurve(nullptr)
   , mType(OverSampleType::None)
 {
   mozilla::HoldJSObjects(this);
 
   WaveShaperNodeEngine* engine = new WaveShaperNodeEngine(this);
   mStream = AudioNodeStream::Create(aContext, engine,
-                                    AudioNodeStream::NO_STREAM_FLAGS);
+                                    AudioNodeStream::NO_STREAM_FLAGS,
+                                    aContext->Graph());
 }
 
 WaveShaperNode::~WaveShaperNode()
 {
   ClearCurve();
 }
 
 void
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -186,73 +186,16 @@ nsCSPParser::resetCurChar(const nsAStrin
 // number sign ("#") character, or by the end of the URI.
 // http://tools.ietf.org/html/rfc3986#section-3.3
 bool
 nsCSPParser::atEndOfPath()
 {
   return (atEnd() || peek(QUESTIONMARK) || peek(NUMBER_SIGN));
 }
 
-void
-nsCSPParser::percentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr)
-{
-  outDecStr.Truncate();
-
-  // helper function that should not be visible outside this methods scope
-  struct local {
-    static inline char16_t convertHexDig(char16_t aHexDig) {
-      if (isNumberToken(aHexDig)) {
-        return aHexDig - '0';
-      }
-      if (aHexDig >= 'A' && aHexDig <= 'F') {
-        return aHexDig - 'A' + 10;
-      }
-      // must be a lower case character
-      // (aHexDig >= 'a' && aHexDig <= 'f')
-      return aHexDig - 'a' + 10;
-    }
-  };
-
-  const char16_t *cur, *end, *hexDig1, *hexDig2;
-  cur = aEncStr.BeginReading();
-  end = aEncStr.EndReading();
-
-  while (cur != end) {
-    // if it's not a percent sign then there is
-    // nothing to do for that character
-    if (*cur != PERCENT_SIGN) {
-      outDecStr.Append(*cur);
-      cur++;
-      continue;
-    }
-
-    // get the two hexDigs following the '%'-sign
-    hexDig1 = cur + 1;
-    hexDig2 = cur + 2;
-
-    // if there are no hexdigs after the '%' then
-    // there is nothing to do for us.
-    if (hexDig1 == end || hexDig2 == end ||
-        !isValidHexDig(*hexDig1) ||
-        !isValidHexDig(*hexDig2)) {
-      outDecStr.Append(PERCENT_SIGN);
-      cur++;
-      continue;
-    }
-
-    // decode "% hexDig1 hexDig2" into a character.
-    char16_t decChar = (local::convertHexDig(*hexDig1) << 4) +
-                       local::convertHexDig(*hexDig2);
-    outDecStr.Append(decChar);
-
-    // increment 'cur' to after the second hexDig
-    cur = ++hexDig2;
-  }
-}
-
 // unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
 bool
 nsCSPParser::atValidUnreservedChar()
 {
   return (peek(isCharacterToken) || peek(isNumberToken) ||
           peek(DASH) || peek(DOT) ||
           peek(UNDERLINE) || peek(TILDE));
 }
@@ -393,17 +336,17 @@ nsCSPParser::subPath(nsCSPHostSrc* aCspH
   uint32_t charCounter = 0;
   nsString pctDecodedSubPath;
 
   while (!atEndOfPath()) {
     if (peek(SLASH)) {
       // before appendig any additional portion of a subpath we have to pct-decode
       // that portion of the subpath. atValidPathChar() already verified a correct
       // pct-encoding, now we can safely decode and append the decoded-sub path.
-      percentDecodeStr(mCurValue, pctDecodedSubPath);
+      CSP_PercentDecodeStr(mCurValue, pctDecodedSubPath);
       aCspHost->appendPath(pctDecodedSubPath);
       // Resetting current value since we are appending parts of the path
       // to aCspHost, e.g; "http://www.example.com/path1/path2" then the
       // first part is "/path1", second part "/path2"
       resetCurValue();
     }
     else if (!atValidPathChar()) {
       const char16_t* params[] = { mCurToken.get() };
@@ -422,17 +365,17 @@ nsCSPParser::subPath(nsCSPHostSrc* aCspH
     advance();
     if (++charCounter > kSubHostPathCharacterCutoff) {
       return false;
     }
   }
   // before appendig any additional portion of a subpath we have to pct-decode
   // that portion of the subpath. atValidPathChar() already verified a correct
   // pct-encoding, now we can safely decode and append the decoded-sub path.
-  percentDecodeStr(mCurValue, pctDecodedSubPath);
+  CSP_PercentDecodeStr(mCurValue, pctDecodedSubPath);
   aCspHost->appendPath(pctDecodedSubPath);
   resetCurValue();
   return true;
 }
 
 bool
 nsCSPParser::path(nsCSPHostSrc* aCspHost)
 {
--- a/dom/security/nsCSPParser.h
+++ b/dom/security/nsCSPParser.h
@@ -139,18 +139,16 @@ class nsCSPParser {
     bool                port();
     bool                path(nsCSPHostSrc* aCspHost);
 
     bool subHost();                                         // helper function to parse subDomains
     bool atValidUnreservedChar();                           // helper function to parse unreserved
     bool atValidSubDelimChar();                             // helper function to parse sub-delims
     bool atValidPctEncodedChar();                           // helper function to parse pct-encoded
     bool subPath(nsCSPHostSrc* aCspHost);                   // helper function to parse paths
-    void percentDecodeStr(const nsAString& aEncStr,         // helper function to percent-decode
-                          nsAString& outDecStr);
 
     inline bool atEnd()
     {
       return mCurChar >= mEndChar;
     }
 
     inline bool accept(char16_t aSymbol)
     {
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -26,16 +26,73 @@ GetCspUtilsLog()
   static mozilla::LazyLogModule gCspUtilsPRLog("CSPUtils");
   return gCspUtilsPRLog;
 }
 
 #define CSPUTILSLOG(args) MOZ_LOG(GetCspUtilsLog(), mozilla::LogLevel::Debug, args)
 #define CSPUTILSLOGENABLED() MOZ_LOG_TEST(GetCspUtilsLog(), mozilla::LogLevel::Debug)
 
 void
+CSP_PercentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr)
+{
+  outDecStr.Truncate();
+
+  // helper function that should not be visible outside this methods scope
+  struct local {
+    static inline char16_t convertHexDig(char16_t aHexDig) {
+      if (isNumberToken(aHexDig)) {
+        return aHexDig - '0';
+      }
+      if (aHexDig >= 'A' && aHexDig <= 'F') {
+        return aHexDig - 'A' + 10;
+      }
+      // must be a lower case character
+      // (aHexDig >= 'a' && aHexDig <= 'f')
+      return aHexDig - 'a' + 10;
+    }
+  };
+
+  const char16_t *cur, *end, *hexDig1, *hexDig2;
+  cur = aEncStr.BeginReading();
+  end = aEncStr.EndReading();
+
+  while (cur != end) {
+    // if it's not a percent sign then there is
+    // nothing to do for that character
+    if (*cur != PERCENT_SIGN) {
+      outDecStr.Append(*cur);
+      cur++;
+      continue;
+    }
+
+    // get the two hexDigs following the '%'-sign
+    hexDig1 = cur + 1;
+    hexDig2 = cur + 2;
+
+    // if there are no hexdigs after the '%' then
+    // there is nothing to do for us.
+    if (hexDig1 == end || hexDig2 == end ||
+        !isValidHexDig(*hexDig1) ||
+        !isValidHexDig(*hexDig2)) {
+      outDecStr.Append(PERCENT_SIGN);
+      cur++;
+      continue;
+    }
+
+    // decode "% hexDig1 hexDig2" into a character.
+    char16_t decChar = (local::convertHexDig(*hexDig1) << 4) +
+                       local::convertHexDig(*hexDig2);
+    outDecStr.Append(decChar);
+
+    // increment 'cur' to after the second hexDig
+    cur = ++hexDig2;
+  }
+}
+
+void
 CSP_GetLocalizedStr(const char16_t* aName,
                     const char16_t** aParams,
                     uint32_t aLength,
                     char16_t** outResult)
 {
   nsCOMPtr<nsIStringBundle> keyStringBundle;
   nsCOMPtr<nsIStringBundleService> stringBundleService =
     mozilla::services::GetStringBundleService();
@@ -562,30 +619,33 @@ nsCSPHostSrc::permits(nsIURI* aUri, cons
   }
 
   // Before we can check if the host matches, we have to
   // extract the host part from aUri.
   nsAutoCString uriHost;
   nsresult rv = aUri->GetHost(uriHost);
   NS_ENSURE_SUCCESS(rv, false);
 
+  nsString decodedUriHost;
+  CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriHost), decodedUriHost);
+
   // 4.5) host matching: Check if the allowed host starts with a wilcard.
   if (mHost.First() == '*') {
     NS_ASSERTION(mHost[1] == '.', "Second character needs to be '.' whenever host starts with '*'");
 
     // Eliminate leading "*", but keeping the FULL STOP (.) thereafter before checking
     // if the remaining characters match
     nsString wildCardHost = mHost;
     wildCardHost = Substring(wildCardHost, 1, wildCardHost.Length() - 1);
-    if (!StringEndsWith(NS_ConvertUTF8toUTF16(uriHost), wildCardHost)) {
+    if (!StringEndsWith(decodedUriHost, wildCardHost)) {
       return false;
     }
   }
   // 4.6) host matching: Check if hosts match.
-  else if (!mHost.Equals(NS_ConvertUTF8toUTF16(uriHost))) {
+  else if (!mHost.Equals(decodedUriHost)) {
     return false;
   }
 
   // Port matching: Check if the ports match.
   if (!permitsPort(mScheme, mPort, aUri)) {
     return false;
   }
 
@@ -599,28 +659,32 @@ nsCSPHostSrc::permits(nsIURI* aUri, cons
     nsCOMPtr<nsIURL> url = do_QueryInterface(aUri);
     if (!url) {
       NS_ASSERTION(false, "can't QI into nsIURI");
       return false;
     }
     nsAutoCString uriPath;
     rv = url->GetFilePath(uriPath);
     NS_ENSURE_SUCCESS(rv, false);
+
+    nsString decodedUriPath;
+    CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriPath), decodedUriPath);
+
     // check if the last character of mPath is '/'; if so
     // we just have to check loading resource is within
     // the allowed path.
     if (mPath.Last() == '/') {
-      if (!StringBeginsWith(NS_ConvertUTF8toUTF16(uriPath), mPath)) {
+      if (!StringBeginsWith(decodedUriPath, mPath)) {
         return false;
       }
     }
     // otherwise mPath whitelists a specific file, and we have to
     // check if the loading resource matches that whitelisted file.
     else {
-      if (!mPath.Equals(NS_ConvertUTF8toUTF16(uriPath))) {
+      if (!mPath.Equals(decodedUriPath)) {
         return false;
       }
     }
   }
 
   // At the end: scheme, host, port and path match -> allow the load.
   return true;
 }
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -188,16 +188,18 @@ nsCSPHostSrc* CSP_CreateHostSrcFromURI(n
 bool CSP_IsValidDirective(const nsAString& aDir);
 bool CSP_IsDirective(const nsAString& aValue, CSPDirective aDir);
 bool CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey);
 bool CSP_IsQuotelessKeyword(const nsAString& aKey);
 CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType);
 
 class nsCSPSrcVisitor;
 
+void CSP_PercentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr);
+
 /* =============== nsCSPSrc ================== */
 
 class nsCSPBaseSrc {
   public:
     nsCSPBaseSrc();
     virtual ~nsCSPBaseSrc();
 
     virtual bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_bug1229639.html
@@ -0,0 +1,7 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+  <body>
+    <!-- this should be allowed -->
+    <script src="http://mochi.test:8888/tests/dom/security/test/csp/%24.js"> </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_bug1229639.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: "default-src 'self'; script-src http://mochi.test:8888/tests/dom/security/test/csp/%24.js
\ No newline at end of file
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -76,16 +76,18 @@ support-files =
   file_redirects_resource.sjs
   file_bug910139.sjs
   file_bug910139.xml
   file_bug910139.xsl
   file_bug909029_star.html
   file_bug909029_star.html^headers^
   file_bug909029_none.html
   file_bug909029_none.html^headers^
+  file_bug1229639.html
+  file_bug1229639.html^headers^
   file_policyuri_regression_from_multipolicy.html
   file_policyuri_regression_from_multipolicy.html^headers^
   file_policyuri_regression_from_multipolicy_policy
   file_shouldprocess.html
   file_nonce_source.html
   file_nonce_source.html^headers^
   file_bug941404.html
   file_bug941404_xhr.html
@@ -203,16 +205,17 @@ skip-if = (buildapp == 'b2g' && (toolkit
 [test_inlinescript.html]
 [test_inlinestyle.html]
 [test_invalid_source_expression.html]
 [test_bug836922_npolicies.html]
 [test_bug886164.html]
 [test_redirects.html]
 [test_bug910139.html]
 [test_bug909029.html]
+[test_bug1229639.html]
 [test_policyuri_regression_from_multipolicy.html]
 [test_nonce_source.html]
 [test_bug941404.html]
 [test_form-action.html]
 skip-if = buildapp == 'b2g' # http-on-opening-request observers are not available in child processes
 [test_hash_source.html]
 skip-if = buildapp == 'b2g' # can't compute hashes in child process (bug 958702)
 [test_scheme_relative_sources.html]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/test_bug1229639.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Bug 1229639 - Percent encoded CSP path matching.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire.
+function examiner() {
+  SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+  SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+
+examiner.prototype  = {
+  observe: function(subject, topic, data) {
+    if (data === 'http://mochi.test:8888/tests/dom/security/test/csp/%24.js') {
+      is(topic, "specialpowers-http-notify-request");
+      this.remove();
+      SimpleTest.finish();
+    }
+  },
+
+  // must eventually call this to remove the listener,
+  // or mochitests might get borked.
+  remove: function() {
+    SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+    SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+  }
+}
+
+window.examiner = new examiner();
+
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('cspframe').src = 'file_bug1229639.html';
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/smil/nsSMILAnimationFunction.h
+++ b/dom/smil/nsSMILAnimationFunction.h
@@ -266,17 +266,17 @@ public:
       }
   };
 
 protected:
   // Typedefs
   typedef FallibleTArray<nsSMILValue> nsSMILValueArray;
 
   // Types
-  enum nsSMILCalcMode
+  enum nsSMILCalcMode : uint8_t
   {
     CALC_LINEAR,
     CALC_DISCRETE,
     CALC_PACED,
     CALC_SPLINE
   };
 
   // Used for sorting nsSMILAnimationFunctions
--- a/dom/smil/nsSMILTimedElement.h
+++ b/dom/smil/nsSMILTimedElement.h
@@ -573,25 +573,25 @@ protected:
   nsSMILTimeValue                 mSimpleDur;
 
   nsSMILRepeatCount               mRepeatCount;
   nsSMILTimeValue                 mRepeatDur;
 
   nsSMILTimeValue                 mMin;
   nsSMILTimeValue                 mMax;
 
-  enum nsSMILFillMode
+  enum nsSMILFillMode : uint8_t
   {
     FILL_REMOVE,
     FILL_FREEZE
   };
   nsSMILFillMode                  mFillMode;
   static nsAttrValue::EnumTable   sFillModeTable[];
 
-  enum nsSMILRestartMode
+  enum nsSMILRestartMode : uint8_t
   {
     RESTART_ALWAYS,
     RESTART_WHENNOTACTIVE,
     RESTART_NEVER
   };
   nsSMILRestartMode               mRestartMode;
   static nsAttrValue::EnumTable   sRestartModeTable[];
 
--- a/dom/xbl/nsXBLPrototypeHandler.cpp
+++ b/dom/xbl/nsXBLPrototypeHandler.cpp
@@ -732,16 +732,17 @@ nsXBLPrototypeHandler::ConstructPrototyp
                                           const char16_t* aGroup,
                                           const char16_t* aPreventDefault,
                                           const char16_t* aAllowUntrusted)
 {
   mType = 0;
 
   if (aKeyElement) {
     mType |= NS_HANDLER_TYPE_XUL;
+    MOZ_ASSERT(!mPrototypeBinding);
     nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(aKeyElement);
     if (!weak) {
       return;
     }
     weak.swap(mHandlerElement);
   }
   else {
     mType |= aCommand ? NS_HANDLER_TYPE_XBL_COMMAND : NS_HANDLER_TYPE_XBL_JS;
@@ -831,22 +832,24 @@ nsXBLPrototypeHandler::ConstructPrototyp
     if (mKeyMask == 0)
       mKeyMask = cAllModifiers;
     ToLowerCase(key);
 
     // We have a charcode.
     mMisc = 1;
     mDetail = key[0];
     const uint8_t GTK2Modifiers = cShift | cControl | cShiftMask | cControlMask;
-    if ((mKeyMask & GTK2Modifiers) == GTK2Modifiers &&
+    if ((mType & NS_HANDLER_TYPE_XUL) &&
+        (mKeyMask & GTK2Modifiers) == GTK2Modifiers &&
         modifiers.First() != char16_t(',') &&
         (mDetail == 'u' || mDetail == 'U'))
       ReportKeyConflict(key.get(), modifiers.get(), aKeyElement, "GTK2Conflict2");
     const uint8_t WinModifiers = cControl | cAlt | cControlMask | cAltMask;
-    if ((mKeyMask & WinModifiers) == WinModifiers &&
+    if ((mType & NS_HANDLER_TYPE_XUL) &&
+        (mKeyMask & WinModifiers) == WinModifiers &&
         modifiers.First() != char16_t(',') &&
         (('A' <= mDetail && mDetail <= 'Z') ||
          ('a' <= mDetail && mDetail <= 'z')))
       ReportKeyConflict(key.get(), modifiers.get(), aKeyElement, "WinConflict2");
   }
   else {
     key.Assign(aKeyCode);
     if (mType & NS_HANDLER_TYPE_XUL)
@@ -880,17 +883,17 @@ void
 nsXBLPrototypeHandler::ReportKeyConflict(const char16_t* aKey, const char16_t* aModifiers, nsIContent* aKeyElement, const char *aMessageName)
 {
   nsCOMPtr<nsIDocument> doc;
   if (mPrototypeBinding) {
     nsXBLDocumentInfo* docInfo = mPrototypeBinding->XBLDocumentInfo();
     if (docInfo) {
       doc = docInfo->GetDocument();
     }
-  } else if (aKeyElement) {
+  } else {
     doc = aKeyElement->OwnerDoc();
   }
 
   nsAutoString id;
   aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
   const char16_t* params[] = { aKey, aModifiers, id.get() };
   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                   NS_LITERAL_CSTRING("XBL Prototype Handler"), doc,
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -3045,41 +3045,44 @@ nsLayoutUtils::TranslateViewToWidget(nsP
   nsPoint pt = (aPt +
   viewOffset).ApplyResolution(GetCurrentAPZResolutionScale(aPresContext->PresShell()));
   LayoutDeviceIntPoint relativeToViewWidget(aPresContext->AppUnitsToDevPixels(pt.x),
                                             aPresContext->AppUnitsToDevPixels(pt.y));
   return relativeToViewWidget + WidgetToWidgetOffset(viewWidget, aWidget);
 }
 
 // Combine aNewBreakType with aOrigBreakType, but limit the break types
-// to NS_STYLE_CLEAR_LEFT, RIGHT, BOTH.
-uint8_t
-nsLayoutUtils::CombineBreakType(uint8_t aOrigBreakType,
-                                uint8_t aNewBreakType)
-{
-  uint8_t breakType = aOrigBreakType;
+// to StyleClear::Left, Right, Both.
+StyleClear
+nsLayoutUtils::CombineBreakType(StyleClear aOrigBreakType,
+                                StyleClear aNewBreakType)
+{
+  StyleClear breakType = aOrigBreakType;
   switch(breakType) {
-  case NS_STYLE_CLEAR_LEFT:
-    if (NS_STYLE_CLEAR_RIGHT == aNewBreakType ||
-        NS_STYLE_CLEAR_BOTH == aNewBreakType) {
-      breakType = NS_STYLE_CLEAR_BOTH;
-    }
-    break;
-  case NS_STYLE_CLEAR_RIGHT:
-    if (NS_STYLE_CLEAR_LEFT == aNewBreakType ||
-        NS_STYLE_CLEAR_BOTH == aNewBreakType) {
-      breakType = NS_STYLE_CLEAR_BOTH;
-    }
-    break;
-  case NS_STYLE_CLEAR_NONE:
-    if (NS_STYLE_CLEAR_LEFT == aNewBreakType ||
-        NS_STYLE_CLEAR_RIGHT == aNewBreakType ||
-        NS_STYLE_CLEAR_BOTH == aNewBreakType) {
-      breakType = aNewBreakType;
-    }
+    case StyleClear::Left:
+      if (StyleClear::Right == aNewBreakType ||
+          StyleClear::Both == aNewBreakType) {
+        breakType = StyleClear::Both;
+      }
+      break;
+    case StyleClear::Right:
+      if (StyleClear::Left == aNewBreakType ||
+          StyleClear::Both == aNewBreakType) {
+        breakType = StyleClear::Both;
+      }
+      break;
+    case StyleClear::None_:
+      if (StyleClear::Left == aNewBreakType ||
+          StyleClear::Right == aNewBreakType ||
+          StyleClear::Both == aNewBreakType) {
+        breakType = aNewBreakType;
+      }
+      break;
+    default:
+      break;
   }
   return breakType;
 }
 
 #ifdef MOZ_DUMP_PAINTING
 #include <stdio.h>
 
 static bool gDumpEventList = false;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -689,18 +689,19 @@ public:
 
   /**
    * If this frame is a placeholder for a float, then return the float,
    * otherwise return nullptr.  aPlaceholder must be a placeholder frame.
    */
   static nsIFrame* GetFloatFromPlaceholder(nsIFrame* aPlaceholder);
 
   // Combine aNewBreakType with aOrigBreakType, but limit the break types
-  // to NS_STYLE_CLEAR_LEFT, RIGHT, LEFT_AND_RIGHT.
-  static uint8_t CombineBreakType(uint8_t aOrigBreakType, uint8_t aNewBreakType);
+  // to StyleClear::Left, Right, Both.
+  static mozilla::StyleClear CombineBreakType(mozilla::StyleClear aOrigBreakType,
+                                              mozilla::StyleClear aNewBreakType);
 
   /**
    * Get the coordinates of a given DOM mouse event, relative to a given
    * frame. Works only for DOM events generated by WidgetGUIEvents.
    * @param aDOMEvent the event
    * @param aFrame the frame to make coordinates relative to
    * @return the point, or (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if
    * for some reason the coordinates for the mouse are not known (e.g.,
--- a/layout/generic/BlockReflowInput.cpp
+++ b/layout/generic/BlockReflowInput.cpp
@@ -39,17 +39,17 @@ BlockReflowInput::BlockReflowInput(const
     mPresContext(aPresContext),
     mReflowInput(aReflowInput),
     mContentArea(aReflowInput.GetWritingMode()),
     mPushedFloats(nullptr),
     mOverflowTracker(nullptr),
     mBorderPadding(mReflowInput.ComputedLogicalBorderPadding()),
     mPrevBEndMargin(),
     mLineNumber(0),
-    mFloatBreakType(NS_STYLE_CLEAR_NONE),
+    mFloatBreakType(StyleClear::None_),
     mConsumedBSize(aConsumedBSize)
 {
   if (!sFloatFragmentsInsideColumnPrefCached) {
     sFloatFragmentsInsideColumnPrefCached = true;
     Preferences::AddBoolVarCache(&sFloatFragmentsInsideColumnEnabled,
                                  "layout.float-fragments-inside-column.enabled");
   }
   mFlags.mFloatFragmentsInsideColumnEnabled = sFloatFragmentsInsideColumnEnabled;
@@ -727,17 +727,17 @@ BlockReflowInput::FlowAndPlaceFloat(nsIF
 
   // Enforce CSS2 9.5.1 rule [2], i.e., make sure that a float isn't
   // ``above'' another float that preceded it in the flow.
   mBCoord = std::max(mFloatManager->GetLowestFloatTop(), mBCoord);
 
   // See if the float should clear any preceding floats...
   // XXX We need to mark this float somehow so that it gets reflowed
   // when floats are inserted before it.
-  if (NS_STYLE_CLEAR_NONE != floatDisplay->mBreakType) {
+  if (StyleClear::None_ != floatDisplay->mBreakType) {
     // XXXldb Does this handle vertical margins correctly?
     mBCoord = ClearFloats(mBCoord, floatDisplay->PhysicalBreakType(wm));
   }
     // Get the band of available space
   nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(mBCoord);
   LogicalRect adjustedAvailableSpace =
     mBlock->AdjustFloatAvailableSpace(*this, floatAvailableSpace.mRect, aFloat);
 
@@ -1075,40 +1075,40 @@ BlockReflowInput::PlaceBelowCurrentLineF
       delete fc;
       aLine->SetHadFloatPushed();
     }
     fc = next;
   }
 }
 
 nscoord
-BlockReflowInput::ClearFloats(nscoord aBCoord, uint8_t aBreakType,
+BlockReflowInput::ClearFloats(nscoord aBCoord, StyleClear aBreakType,
                                 nsIFrame *aReplacedBlock,
                                 uint32_t aFlags)
 {
 #ifdef DEBUG
   if (nsBlockFrame::gNoisyReflow) {
     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
     printf("clear floats: in: aBCoord=%d\n", aBCoord);
   }
 #endif
 
 #ifdef NOISY_FLOAT_CLEARING
-  printf("BlockReflowInput::ClearFloats: aBCoord=%d breakType=%d\n",
-         aBCoord, aBreakType);
+  printf("BlockReflowInput::ClearFloats: aBCoord=%d breakType=%s\n",
+         aBCoord, nsLineBox::BreakTypeToString(aBreakType));
   mFloatManager->List(stdout);
 #endif
 
   if (!mFloatManager->HasAnyFloats()) {
     return aBCoord;
   }
 
   nscoord newBCoord = aBCoord;
 
-  if (aBreakType != NS_STYLE_CLEAR_NONE) {
+  if (aBreakType != StyleClear::None_) {
     newBCoord = mFloatManager->ClearFloats(newBCoord, aBreakType, aFlags);
   }
 
   if (aReplacedBlock) {
     for (;;) {
       nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(newBCoord);
       if (ReplacedBlockFitsInAvailSpace(aReplacedBlock, floatAvailableSpace)) {
         break;
--- a/layout/generic/BlockReflowInput.h
+++ b/layout/generic/BlockReflowInput.h
@@ -144,17 +144,17 @@ public:
   bool FlowAndPlaceFloat(nsIFrame* aFloat);
 
   void PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aFloats,
                                    nsLineBox* aLine);
 
   // Returns the first coordinate >= aBCoord that clears the
   // floats indicated by aBreakType and has enough inline size between floats
   // (or no floats remaining) to accomodate aReplacedBlock.
-  nscoord ClearFloats(nscoord aBCoord, uint8_t aBreakType,
+  nscoord ClearFloats(nscoord aBCoord, mozilla::StyleClear aBreakType,
                       nsIFrame *aReplacedBlock = nullptr,
                       uint32_t aFlags = 0);
 
   // Advances to the next band, i.e., the next horizontal stripe in
   // which there is a different set of floats.
   // Return false if it did not advance, which only happens for
   // constrained heights (and means that we should get pushed to the
   // next column/page).
@@ -366,17 +366,17 @@ public:
   nsFloatCacheFreeList mBelowCurrentLineFloats;
 
   nscoord mMinLineHeight;
 
   int32_t mLineNumber;
 
   Flags mFlags;
 
-  uint8_t mFloatBreakType;
+  StyleClear mFloatBreakType;
 
   // The amount of computed block-direction size "consumed" by previous-in-flows.
   nscoord mConsumedBSize;
 
 private:
   bool CanPlaceFloat(nscoord aFloatISize,
                      const nsFlowAreaRect& aFloatAvailableSpace);
 
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -2009,24 +2009,25 @@ nsStyleDisplay::PhysicalFloats(mozilla::
     return aWM.IsBidiLTR() ? StyleFloat::Left : StyleFloat::Right;
   }
   if (mFloat == StyleFloat::InlineEnd) {
     return aWM.IsBidiLTR() ? StyleFloat::Right : StyleFloat::Left;
   }
   return mFloat;
 }
 
-inline uint8_t
+inline mozilla::StyleClear
 nsStyleDisplay::PhysicalBreakType(mozilla::WritingMode aWM) const
 {
-  if (mBreakType == NS_STYLE_CLEAR_INLINE_START) {
-    return aWM.IsBidiLTR() ? NS_STYLE_CLEAR_LEFT : NS_STYLE_CLEAR_RIGHT;
+  using StyleClear = mozilla::StyleClear;
+  if (mBreakType == StyleClear::InlineStart) {
+    return aWM.IsBidiLTR() ? StyleClear::Left : StyleClear::Right;
   }
-  if (mBreakType == NS_STYLE_CLEAR_INLINE_END) {
-    return aWM.IsBidiLTR() ? NS_STYLE_CLEAR_RIGHT : NS_STYLE_CLEAR_LEFT;
+  if (mBreakType == StyleClear::InlineEnd) {
+    return aWM.IsBidiLTR() ? StyleClear::Right : StyleClear::Left;
   }
   return mBreakType;
 }
 
 inline bool
 nsStyleMargin::HasBlockAxisAuto(mozilla::WritingMode aWM) const
 {
   return mMargin.HasBlockAxisAuto(aWM);
--- a/layout/generic/nsBRFrame.cpp
+++ b/layout/generic/nsBRFrame.cpp
@@ -139,19 +139,19 @@ BRFrame::Reflow(nsPresContext* aPresCont
       // XXX This also fixes bug 10036!
       // Warning: nsTextControlFrame::CalculateSizeStandard depends on
       // the following line, see bug 228752.
       // The code below in AddInlinePrefISize also adds 1 appunit to width
       finalSize.ISize(wm) = 1;
     }
 
     // Return our reflow status
-    uint32_t breakType = aReflowInput.mStyleDisplay->PhysicalBreakType(wm);
-    if (NS_STYLE_CLEAR_NONE == breakType) {
-      breakType = NS_STYLE_CLEAR_LINE;
+    StyleClear breakType = aReflowInput.mStyleDisplay->PhysicalBreakType(wm);
+    if (StyleClear::None_ == breakType) {
+      breakType = StyleClear::Line;
     }
 
     aStatus = NS_INLINE_BREAK | NS_INLINE_BREAK_AFTER |
       NS_INLINE_MAKE_BREAK_TYPE(breakType);
     ll->SetLineEndsInBR(true);
   }
   else {
     aStatus = NS_FRAME_COMPLETE;
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -1544,17 +1544,17 @@ nsBlockFrame::ComputeFinalSize(const Ref
         std::min(blockEndEdgeOfChildren + aState.mPrevBEndMargin.get(),
                aState.mReflowInput.AvailableBSize());
     }
   }
   if (aState.mFlags.mBlockNeedsFloatManager) {
     // Include the float manager's state to properly account for the
     // block-end margin of any floated elements; e.g., inside a table cell.
     nscoord floatHeight =
-      aState.ClearFloats(blockEndEdgeOfChildren, NS_STYLE_CLEAR_BOTH,
+      aState.ClearFloats(blockEndEdgeOfChildren, StyleClear::Both,
                          nullptr, nsFloatManager::DONT_CLEAR_PUSHED_FLOATS);
     blockEndEdgeOfChildren = std::max(blockEndEdgeOfChildren, floatHeight);
   }
 
   if (NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedBSize()
       && (GetParent()->GetType() != nsGkAtoms::columnSetFrame ||
           aReflowInput.mParentReflowInput->AvailableBSize() == NS_UNCONSTRAINEDSIZE)) {
     ComputeFinalBSize(aReflowInput, &aState.mReflowStatus,
@@ -1914,24 +1914,25 @@ nsBlockFrame::PrepareResizeReflow(BlockR
       if (!line->IsBlock()) {
         printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n", 
                line.get(), line->IsImpactedByFloat() ? "" : "not ");
       }
 #endif
 #ifdef DEBUG
       if (gNoisyReflow && !line->IsDirty()) {
         IndentBy(stdout, gNoiseIndent + 1);
-        printf("skipped: line=%p next=%p %s %s%s%s breakTypeBefore/After=%d/%d xmost=%d\n",
+        printf("skipped: line=%p next=%p %s %s%s%s breakTypeBefore/After=%s/%s xmost=%d\n",
            static_cast<void*>(line.get()),
            static_cast<void*>((line.next() != end_lines() ? line.next().get() : nullptr)),
            line->IsBlock() ? "block" : "inline",
            line->HasBreakAfter() ? "has-break-after " : "",
            line->HasFloats() ? "has-floats " : "",
            line->IsImpactedByFloat() ? "impacted " : "",
-           line->GetBreakTypeBefore(), line->GetBreakTypeAfter(),
+           line->BreakTypeToString(line->GetBreakTypeBefore()),
+           line->BreakTypeToString(line->GetBreakTypeAfter()),
            line->IEnd());
       }
 #endif
     }
   }
   else {
     // Mark everything dirty
     for (line_iterator line = begin_lines(), line_end = end_lines();
@@ -2024,17 +2025,17 @@ nsBlockFrame::PropagateFloatDamage(Block
         aLine->MarkDirty();
       }
     }
   }
 }
 
 static bool LineHasClear(nsLineBox* aLine) {
   return aLine->IsBlock()
-    ? (aLine->GetBreakTypeBefore() ||
+    ? (aLine->GetBreakTypeBefore() != StyleClear::None_ ||
        (aLine->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN) ||
        !nsBlockFrame::BlockCanIntersectFloats(aLine->mFirstChild))
     : aLine->HasFloatBreakAfter();
 }
 
 
 /**
  * Reparent a whole list of floats from aOldParent to this block.  The
@@ -2075,17 +2076,17 @@ static void DumpLine(const BlockReflowIn
 #endif
 }
 
 void
 nsBlockFrame::ReflowDirtyLines(BlockReflowInput& aState)
 {
   bool keepGoing = true;
   bool repositionViews = false; // should we really need this?
-  bool foundAnyClears = aState.mFloatBreakType != NS_STYLE_CLEAR_NONE;
+  bool foundAnyClears = aState.mFloatBreakType != StyleClear::None_;
   bool willReflowAgain = false;
 
 #ifdef DEBUG
   if (gNoisyReflow) {
     IndentBy(stdout, gNoiseIndent);
     ListTag(stdout);
     printf(": reflowing dirty lines");
     printf(" computedISize=%d\n", aState.mReflowInput.ComputedISize());
@@ -2116,17 +2117,17 @@ nsBlockFrame::ReflowDirtyLines(BlockRefl
     // recompute the carried out margin before the line if we want to
     // reflow it or if its previous margin is dirty
   bool needToRecoverState = false;
     // Float continuations were reflowed in ReflowPushedFloats
   bool reflowedFloat = mFloats.NotEmpty() &&
     (mFloats.FirstChild()->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
   bool lastLineMovedUp = false;
   // We save up information about BR-clearance here
-  uint8_t inlineFloatBreakType = aState.mFloatBreakType;
+  StyleClear inlineFloatBreakType = aState.mFloatBreakType;
 
   line_iterator line = begin_lines(), line_end = end_lines();
 
   // Reflow the lines that are already ours
   for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) {
     DumpLine(aState, line, deltaBCoord, 0);
 #ifdef DEBUG
     AutoNoisyIndenter indent2(gNoisyReflow);
@@ -2147,22 +2148,22 @@ nsBlockFrame::ReflowDirtyLines(BlockRefl
     if (line->IsBlock() &&
         !nsBlockFrame::BlockCanIntersectFloats(line->mFirstChild)) {
       replacedBlock = line->mFirstChild;
     }
 
     // We have to reflow the line if it's a block whose clearance
     // might have changed, so detect that.
     if (!line->IsDirty() &&
-        (line->GetBreakTypeBefore() != NS_STYLE_CLEAR_NONE ||
+        (line->GetBreakTypeBefore() != StyleClear::None_ ||
          replacedBlock)) {
       nscoord curBCoord = aState.mBCoord;
       // See where we would be after applying any clearance due to
       // BRs.
-      if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
+      if (inlineFloatBreakType != StyleClear::None_) {
         curBCoord = aState.ClearFloats(curBCoord, inlineFloatBreakType);
       }
 
       nscoord newBCoord =
         aState.ClearFloats(curBCoord, line->GetBreakTypeBefore(), replacedBlock);
 
       if (line->HasClearance()) {
         // Reflow the line if it might not have clearance anymore.
@@ -2178,24 +2179,24 @@ nsBlockFrame::ReflowDirtyLines(BlockRefl
         // Reflow the line if the line might have clearance now.
         if (curBCoord != newBCoord) {
           line->MarkDirty();
         }
       }
     }
 
     // We might have to reflow a line that is after a clearing BR.
-    if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
+    if (inlineFloatBreakType != StyleClear::None_) {
       aState.mBCoord = aState.ClearFloats(aState.mBCoord, inlineFloatBreakType);
       if (aState.mBCoord != line->BStart() + deltaBCoord) {
         // SlideLine is not going to put the line where the clearance
         // put it. Reflow the line to be sure.
         line->MarkDirty();
       }
-      inlineFloatBreakType = NS_STYLE_CLEAR_NONE;
+      inlineFloatBreakType = StyleClear::None_;
     }
 
     bool previousMarginWasDirty = line->IsPreviousMarginDirty();
     if (previousMarginWasDirty) {
       // If the previous margin is dirty, reflow the current line
       line->MarkDirty();
       line->ClearPreviousMarginDirty();
     } else if (line->BEnd() + deltaBCoord > aState.mBEndEdge) {
@@ -2438,17 +2439,17 @@ nsBlockFrame::ReflowDirtyLines(BlockRefl
       // sure whether we really want to mark all lines dirty after an
       // interrupt, but until we get better at propagating float damage we
       // really do need to do it this way; see comments inside MarkLineDirty.
       MarkLineDirtyForInterrupt(line);
     }
   }
 
   // Handle BR-clearance from the last line of the block
-  if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
+  if (inlineFloatBreakType != StyleClear::None_) {
     aState.mBCoord = aState.ClearFloats(aState.mBCoord, inlineFloatBreakType);
   }
 
   if (needToRecoverState) {
     // Is this expensive?
     aState.ReconstructMarginBefore(line);
 
     // Update aState.mPrevChild as if we had reflowed all of the frames in
@@ -3121,22 +3122,22 @@ nsBlockFrame::ReflowBlockFrame(BlockRefl
   if (!frame) {
     NS_ASSERTION(false, "program error - unexpected empty line"); 
     return; 
   }
 
   // Prepare the block reflow engine
   nsBlockReflowContext brc(aState.mPresContext, aState.mReflowInput);
 
-  uint8_t breakType = frame->StyleDisplay()->
+  StyleClear breakType = frame->StyleDisplay()->
     PhysicalBreakType(aState.mReflowInput.GetWritingMode());
-  if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
+  if (StyleClear::None_ != aState.mFloatBreakType) {
     breakType = nsLayoutUtils::CombineBreakType(breakType,
                                                 aState.mFloatBreakType);
-    aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
+    aState.mFloatBreakType = StyleClear::None_;
   }
 
   // Clear past floats before the block if the clear style is not none
   aLine->SetBreakTypeBefore(breakType);
 
   // See if we should apply the block-start margin. If the block frame being
   // reflowed is a continuation (non-null prev-in-flow) then we don't
   // apply its block-start margin because it's not significant unless it has
@@ -3149,17 +3150,17 @@ nsBlockFrame::ReflowBlockFrame(BlockRefl
     // The HasClearance setting is only valid if ShouldApplyBStartMargin
     // returned false (in which case the block-start margin-root set our
     // clearance flag). Otherwise clear it now. We'll set it later on
     // ourselves if necessary.
     aLine->ClearHasClearance();
   }
   bool treatWithClearance = aLine->HasClearance();
 
-  bool mightClearFloats = breakType != NS_STYLE_CLEAR_NONE;
+  bool mightClearFloats = breakType != StyleClear::None_;
   nsIFrame *replacedBlock = nullptr;
   if (!nsBlockFrame::BlockCanIntersectFloats(frame)) {
     mightClearFloats = true;
     replacedBlock = frame;
   }
 
   // If our block-start margin was counted as part of some parent's block-start
   // margin collapse, and we are being speculatively reflowed assuming this
@@ -3428,17 +3429,17 @@ nsBlockFrame::ReflowBlockFrame(BlockRefl
                                                 floatAvailableSpace)) {
         // Advance to the next band.
         nscoord newBCoord = aState.mBCoord;
         if (aState.AdvanceToNextBand(floatAvailableSpace.mRect, &newBCoord)) {
           advanced = true;
         }
         // ClearFloats might be able to advance us further once we're there.
         aState.mBCoord =
-          aState.ClearFloats(newBCoord, NS_STYLE_CLEAR_NONE, replacedBlock);
+          aState.ClearFloats(newBCoord, StyleClear::None_, replacedBlock);
         // Start over with a new available space rect at the new height.
         floatAvailableSpace =
           aState.GetFloatAvailableSpaceWithState(aState.mBCoord,
                                                  &floatManagerState);
       }
 
       LogicalRect oldAvailSpace(availSpace);
       aState.ComputeBlockAvailSpace(frame, floatAvailableSpace,
@@ -4049,17 +4050,17 @@ nsBlockFrame::DoReflowInlineFrames(Block
 }
 
 /**
  * Reflow an inline frame. The reflow status is mapped from the frames
  * reflow status to the lines reflow status (not to our reflow status).
  * The line reflow status is simple: true means keep placing frames
  * on the line; false means don't (the line is done). If the line
  * has some sort of breaking affect then aLine's break-type will be set
- * to something other than NS_STYLE_CLEAR_NONE.
+ * to something other than StyleClear::None_.
  */
 void
 nsBlockFrame::ReflowInlineFrame(BlockReflowInput& aState,
                                 nsLineLayout& aLineLayout,
                                 line_iterator aLine,
                                 nsIFrame* aFrame,
                                 LineReflowStatus* aLineReflowStatus)
 {
@@ -4106,28 +4107,27 @@ nsBlockFrame::ReflowInlineFrame(BlockRef
       see bug 22496
    */
 
   // Process the child frames reflow status. There are 5 cases:
   // complete, not-complete, break-before, break-after-complete,
   // break-after-not-complete. There are two situations: we are a
   // block or we are an inline. This makes a total of 10 cases
   // (fortunately, there is some overlap).
-  aLine->SetBreakTypeAfter(NS_STYLE_CLEAR_NONE);
-  if (NS_INLINE_IS_BREAK(frameReflowStatus) || 
-      (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType)) {
+  aLine->SetBreakTypeAfter(StyleClear::None_);
+  if (NS_INLINE_IS_BREAK(frameReflowStatus) ||
+      StyleClear::None_ != aState.mFloatBreakType) {
     // Always abort the line reflow (because a line break is the
     // minimal amount of break we do).
     *aLineReflowStatus = LINE_REFLOW_STOP;
 
     // XXX what should aLine's break-type be set to in all these cases?
-    uint8_t breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus);
-    NS_ASSERTION((NS_STYLE_CLEAR_NONE != breakType) || 
-                 (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType), "bad break type");
-    NS_ASSERTION(NS_STYLE_CLEAR_MAX >= breakType, "invalid break type");
+    StyleClear breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus);
+    MOZ_ASSERT(StyleClear::None_ != breakType ||
+               StyleClear::None_ != aState.mFloatBreakType, "bad break type");
 
     if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
       // Break-before cases.
       if (aFrame == aLine->mFirstChild) {
         // If we break before the first frame on the line then we must
         // be trying to place content where there's no room (e.g. on a
         // line with wide floats). Inform the caller to reflow the
         // line after skipping past a float.
@@ -4142,28 +4142,28 @@ nsBlockFrame::ReflowInlineFrame(BlockRef
         // was pushed, then mark the line as having word wrapped. We need to
         // know that if we're shrink wrapping our width
         if (pushedFrame) {
           aLine->SetLineWrapped(true);
         }
       }
     }
     else {
-      // If a float split and its prev-in-flow was followed by a <BR>, then combine 
-      // the <BR>'s break type with the inline's break type (the inline will be the very 
+      // If a float split and its prev-in-flow was followed by a <BR>, then combine
+      // the <BR>'s break type with the inline's break type (the inline will be the very
       // next frame after the split float).
-      if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
+      if (StyleClear::None_ != aState.mFloatBreakType) {
         breakType = nsLayoutUtils::CombineBreakType(breakType,
                                                     aState.mFloatBreakType);
-        aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
+        aState.mFloatBreakType = StyleClear::None_;
       }
       // Break-after cases
-      if (breakType == NS_STYLE_CLEAR_LINE) {
+      if (breakType == StyleClear::Line) {
         if (!aLineLayout.GetLineEndsInBR()) {
-          breakType = NS_STYLE_CLEAR_NONE;
+          breakType = StyleClear::None_;
         }
       }
       aLine->SetBreakTypeAfter(breakType);
       if (NS_FRAME_IS_COMPLETE(frameReflowStatus)) {
         // Split line, but after the frame just reflowed
         SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
 
         if (NS_INLINE_IS_BREAK_AFTER(frameReflowStatus) &&
@@ -6221,29 +6221,29 @@ nsBlockFrame::ReflowFloat(BlockReflowInp
                     nsDidReflowStatus::FINISHED);
 
 #ifdef NOISY_FLOAT
   printf("end ReflowFloat %p, sized to %d,%d\n",
          aFloat, metrics.Width(), metrics.Height());
 #endif
 }
 
-uint8_t
+StyleClear
 nsBlockFrame::FindTrailingClear()
 {
   // find the break type of the last line
   for (nsIFrame* b = this; b; b = b->GetPrevInFlow()) {
     nsBlockFrame* block = static_cast<nsBlockFrame*>(b);
     line_iterator endLine = block->end_lines();
     if (endLine != block->begin_lines()) {
       --endLine;
       return endLine->GetBreakTypeAfter();
     }
   }
-  return NS_STYLE_CLEAR_NONE;
+  return StyleClear::None_;
 }
 
 void
 nsBlockFrame::ReflowPushedFloats(BlockReflowInput& aState,
                                  nsOverflowAreas&    aOverflowAreas,
                                  nsReflowStatus&     aStatus)
 {
   // Pushed floats live at the start of our float list; see comment
@@ -6302,17 +6302,17 @@ nsBlockFrame::ReflowPushedFloats(BlockRe
       // We didn't push |f| so its next-sibling is next.
       next = f->GetNextSibling();
       prev = f;
     } // else: we did push |f| so |prev|'s new next-sibling is next.
     f = next;
   }
 
   // If there are continued floats, then we may need to continue BR clearance
-  if (0 != aState.ClearFloats(0, NS_STYLE_CLEAR_BOTH)) {
+  if (0 != aState.ClearFloats(0, StyleClear::Both)) {
     nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
     if (prevBlock) {
       aState.mFloatBreakType = prevBlock->FindTrailingClear();
     }
   }
 }
 
 void
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -571,17 +571,17 @@ protected:
   /** Reflow pushed floats
    */
   void ReflowPushedFloats(BlockReflowInput& aState,
                           nsOverflowAreas&    aOverflowAreas,
                           nsReflowStatus&     aStatus);
 
   /** Find any trailing BR clear from the last line of the block (or its PIFs)
    */
-  uint8_t FindTrailingClear();
+  mozilla::StyleClear FindTrailingClear();
 
   /**
    * Remove a float from our float list.
    */
   void RemoveFloat(nsIFrame* aFloat);
   /**
    * Remove a float from the float cache for the line its placeholder is on.
    */
--- a/layout/generic/nsBlockReflowContext.cpp
+++ b/layout/generic/nsBlockReflowContext.cpp
@@ -163,17 +163,17 @@ nsBlockReflowContext::ComputeCollapsedBS
           {
             LogicalSize availSpace =
               outerReflowInput->ComputedSize(kid->GetWritingMode());
             ReflowInput innerReflowInput(prescontext,
                                                *outerReflowInput, kid,
                                                availSpace);
             // Record that we're being optimistic by assuming the kid
             // has no clearance
-            if (kid->StyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE ||
+            if (kid->StyleDisplay()->mBreakType != StyleClear::None_ ||
                 !nsBlockFrame::BlockCanIntersectFloats(kid)) {
               *aMayNeedRetry = true;
             }
             if (ComputeCollapsedBStartMargin(innerReflowInput, aMargin,
                                              aClearanceFrame, aMayNeedRetry,
                                              &isEmpty)) {
               line->MarkDirty();
               dirtiedLine = true;
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -473,59 +473,59 @@ nsFloatManager::List(FILE* out) const
                    fi.LineLeft(), fi.BStart(), fi.ISize(), fi.BSize(),
                    fi.mLeftBEnd, fi.mRightBEnd);
   }
   return NS_OK;
 }
 #endif
 
 nscoord
-nsFloatManager::ClearFloats(nscoord aBCoord, uint8_t aBreakType,
+nsFloatManager::ClearFloats(nscoord aBCoord, StyleClear aBreakType,
                             uint32_t aFlags) const
 {
   if (!(aFlags & DONT_CLEAR_PUSHED_FLOATS) && ClearContinues(aBreakType)) {
     return nscoord_MAX;
   }
   if (!HasAnyFloats()) {
     return aBCoord;
   }
 
   nscoord blockEnd = aBCoord + mBlockStart;
 
   const FloatInfo &tail = mFloats[mFloats.Length() - 1];
   switch (aBreakType) {
-    case NS_STYLE_CLEAR_BOTH:
+    case StyleClear::Both:
       blockEnd = std::max(blockEnd, tail.mLeftBEnd);
       blockEnd = std::max(blockEnd, tail.mRightBEnd);
       break;
-    case NS_STYLE_CLEAR_LEFT:
+    case StyleClear::Left:
       blockEnd = std::max(blockEnd, tail.mLeftBEnd);
       break;
-    case NS_STYLE_CLEAR_RIGHT:
+    case StyleClear::Right:
       blockEnd = std::max(blockEnd, tail.mRightBEnd);
       break;
     default:
       // Do nothing
       break;
   }
 
   blockEnd -= mBlockStart;
 
   return blockEnd;
 }
 
 bool
-nsFloatManager::ClearContinues(uint8_t aBreakType) const
+nsFloatManager::ClearContinues(StyleClear aBreakType) const
 {
   return ((mPushedLeftFloatPastBreak || mSplitLeftFloatAcrossBreak) &&
-          (aBreakType == NS_STYLE_CLEAR_BOTH ||
-           aBreakType == NS_STYLE_CLEAR_LEFT)) ||
+          (aBreakType == StyleClear::Both ||
+           aBreakType == StyleClear::Left)) ||
          ((mPushedRightFloatPastBreak || mSplitRightFloatAcrossBreak) &&
-          (aBreakType == NS_STYLE_CLEAR_BOTH ||
-           aBreakType == NS_STYLE_CLEAR_RIGHT));
+          (aBreakType == StyleClear::Both ||
+           aBreakType == StyleClear::Right));
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // FloatInfo
 
 nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame,
                                      nscoord aLineLeft, nscoord aBStart,
                                      nscoord aISize, nscoord aBSize)
--- a/layout/generic/nsFloatManager.h
+++ b/layout/generic/nsFloatManager.h
@@ -266,24 +266,24 @@ public:
    *
    * Both aBCoord and the result are relative to the current translation.
    */
   enum {
     // Tell ClearFloats not to push to nscoord_MAX when floats have been
     // pushed to the next page/column.
     DONT_CLEAR_PUSHED_FLOATS = (1<<0)
   };
-  nscoord ClearFloats(nscoord aBCoord, uint8_t aBreakType,
+  nscoord ClearFloats(nscoord aBCoord, mozilla::StyleClear aBreakType,
                       uint32_t aFlags = 0) const;
 
   /**
    * Checks if clear would pass into the floats' BFC's next-in-flow,
    * i.e. whether floats affecting this clear have continuations.
    */
-  bool ClearContinues(uint8_t aBreakType) const;
+  bool ClearContinues(mozilla::StyleClear aBreakType) const;
 
   void AssertStateMatches(SavedState *aState) const
   {
     NS_ASSERTION(aState->mLineLeft == mLineLeft &&
                  aState->mBlockStart == mBlockStart &&
                  aState->mPushedLeftFloatPastBreak ==
                    mPushedLeftFloatPastBreak &&
                  aState->mPushedRightFloatPastBreak ==
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4498,29 +4498,29 @@ nsIFrame::InlinePrefISizeData::ForceBrea
             // preferred widths accumulated for floats that have not yet
             // been cleared past
             floats_cur_left = 0,
             floats_cur_right = 0;
 
     for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) {
       const FloatInfo& floatInfo = mFloats[i];
       const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
-      uint8_t breakType = floatDisp->PhysicalBreakType(mLineContainerWM);
-      if (breakType == NS_STYLE_CLEAR_LEFT ||
-          breakType == NS_STYLE_CLEAR_RIGHT ||
-          breakType == NS_STYLE_CLEAR_BOTH) {
+      StyleClear breakType = floatDisp->PhysicalBreakType(mLineContainerWM);
+      if (breakType == StyleClear::Left ||
+          breakType == StyleClear::Right ||
+          breakType == StyleClear::Both) {
         nscoord floats_cur = NSCoordSaturatingAdd(floats_cur_left,
                                                   floats_cur_right);
         if (floats_cur > floats_done) {
           floats_done = floats_cur;
         }
-        if (breakType != NS_STYLE_CLEAR_RIGHT) {
+        if (breakType != StyleClear::Right) {
           floats_cur_left = 0;
         }
-        if (breakType != NS_STYLE_CLEAR_LEFT) {
+        if (breakType != StyleClear::Left) {
           floats_cur_right = 0;
         }
       }
 
       StyleFloat floatStyle = floatDisp->PhysicalFloats(mLineContainerWM);
       nscoord& floats_cur =
         floatStyle == StyleFloat::Left ? floats_cur_left : floats_cur_right;
       nscoord floatWidth = floatInfo.Width();
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -280,35 +280,36 @@ typedef uint32_t nsReflowStatus;
   (0 != ((_status) & NS_INLINE_BREAK))
 
 #define NS_INLINE_IS_BREAK_AFTER(_status) \
   (0 != ((_status) & NS_INLINE_BREAK_AFTER))
 
 #define NS_INLINE_IS_BREAK_BEFORE(_status) \
   (NS_INLINE_BREAK == ((_status) & (NS_INLINE_BREAK|NS_INLINE_BREAK_AFTER)))
 
-#define NS_INLINE_GET_BREAK_TYPE(_status) (((_status) >> 12) & 0xF)
-
-#define NS_INLINE_MAKE_BREAK_TYPE(_type)  ((_type) << 12)
+#define NS_INLINE_GET_BREAK_TYPE(_status) \
+  (static_cast<StyleClear>(((_status) >> 12) & 0xF))
+
+#define NS_INLINE_MAKE_BREAK_TYPE(_type)  (static_cast<int>(_type) << 12)
 
 // Construct a line-break-before status. Note that there is no
 // completion status for a line-break before because we *know* that
 // the frame will be reflowed later and hence its current completion
 // status doesn't matter.
 #define NS_INLINE_LINE_BREAK_BEFORE()                                   \
   (NS_INLINE_BREAK | NS_INLINE_BREAK_BEFORE |                           \
-   NS_INLINE_MAKE_BREAK_TYPE(NS_STYLE_CLEAR_LINE))
+   NS_INLINE_MAKE_BREAK_TYPE(StyleClear::Line))
 
 // Take a completion status and add to it the desire to have a
 // line-break after. For this macro we do need the completion status
 // because the user of the status will need to know whether to
 // continue the frame or not.
 #define NS_INLINE_LINE_BREAK_AFTER(_completionStatus)                   \
   ((_completionStatus) | NS_INLINE_BREAK | NS_INLINE_BREAK_AFTER |      \
-   NS_INLINE_MAKE_BREAK_TYPE(NS_STYLE_CLEAR_LINE))
+   NS_INLINE_MAKE_BREAK_TYPE(StyleClear::Line))
 
 // A frame is "truncated" if the part of the frame before the first
 // possible break point was unable to fit in the available vertical
 // space.  Therefore, the entire frame should be moved to the next page.
 // A frame that begins at the top of the page must never be "truncated".
 // Doing so would likely cause an infinite loop.
 #define NS_FRAME_TRUNCATED  0x0010
 #define NS_FRAME_IS_TRUNCATED(status) \
--- a/layout/generic/nsLineBox.cpp
+++ b/layout/generic/nsLineBox.cpp
@@ -43,23 +43,19 @@ nsLineBox::nsLineBox(nsIFrame* aFrame, i
   ++ctorCount;
   NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child");
   nsIFrame* f = aFrame;
   for (int32_t n = aCount; n > 0; f = f->GetNextSibling(), --n) {
     NS_ASSERTION(aIsBlock == f->IsBlockOutside(),
                  "wrong kind of child frame");
   }
 #endif
-
-  static_assert(NS_STYLE_CLEAR_MAX <= 15,
+  static_assert(static_cast<int>(StyleClear::Max) <= 15,
                 "FlagBits needs more bits to store the full range of "
                 "break type ('clear') values");
-#if NS_STYLE_CLEAR_NONE > 0
-  mFlags.mBreakType = NS_STYLE_CLEAR_NONE;
-#endif
   mChildCount = aCount;
   MarkDirty();
   mFlags.mBlock = aIsBlock;
 }
 
 nsLineBox::~nsLineBox()
 {
   MOZ_COUNT_DTOR(nsLineBox);
@@ -190,27 +186,29 @@ ListFloats(FILE* out, const char* aPrefi
     else {
       str += "\n###!!! NULL out-of-flow frame";
     }
     fprintf_stderr(out, "%s\n", str.get());
     fc = fc->Next();
   }
 }
 
-const char *
-BreakTypeToString(uint8_t aBreakType)
+const char*
+nsLineBox::BreakTypeToString(StyleClear aBreakType) const
 {
   switch (aBreakType) {
-  case NS_STYLE_CLEAR_NONE: return "nobr";
-  case NS_STYLE_CLEAR_LEFT: return "leftbr";
-  case NS_STYLE_CLEAR_RIGHT: return "rightbr";
-  case NS_STYLE_CLEAR_BOTH: return "leftbr+rightbr";
-  case NS_STYLE_CLEAR_LINE: return "linebr";
-  default:
-    break;
+    case StyleClear::None_: return "nobr";
+    case StyleClear::Left: return "leftbr";
+    case StyleClear::Right: return "rightbr";
+    case StyleClear::InlineStart: return "inlinestartbr";
+    case StyleClear::InlineEnd: return "inlineendbr";
+    case StyleClear::Both: return "leftbr+rightbr";
+    case StyleClear::Line: return "linebr";
+    default:
+      break;
   }
   return "unknown";
 }
 
 char*
 nsLineBox::StateToString(char* aBuf, int32_t aBufSize) const
 {
   snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]",
@@ -280,19 +278,17 @@ nsLineBox::List(FILE* out, const char* a
   }
 
   if (HasFloats()) {
     fprintf_stderr(out, "%s> floats <\n", aPrefix);
     ListFloats(out, pfx.get(), mInlineData->mFloats);
   }
   fprintf_stderr(out, "%s>\n", aPrefix);
 }
-#endif
 
-#ifdef DEBUG
 nsIFrame*
 nsLineBox::LastChild() const
 {
   nsIFrame* frame = mFirstChild;
   int32_t n = GetChildCount() - 1;
   while (--n >= 0) {
     frame = frame->GetNextSibling();
   }
--- a/layout/generic/nsLineBox.h
+++ b/layout/generic/nsLineBox.h
@@ -138,17 +138,16 @@ public:
 protected:
   nsFloatCache* mTail;
 
   friend class nsFloatCacheList;
 };
 
 //----------------------------------------------------------------------
 
-#define LINE_MAX_BREAK_TYPE  ((1 << 4) - 1)
 #define LINE_MAX_CHILD_COUNT INT32_MAX
 
 /**
  * Function to create a line box and initialize it with a single frame.
  * The allocation is infallible.
  * If the frame was moved from another line then you're responsible
  * for notifying that line using NoteFrameRemoved().  Alternatively,
  * it's better to use the next function that does that for you in an
@@ -389,47 +388,48 @@ public:
       --mChildCount;
     }
   }
 
   // mBreakType value
   // Break information is applied *before* the line if the line is a block,
   // or *after* the line if the line is an inline. Confusing, I know, but
   // using different names should help.
+  using StyleClear = mozilla::StyleClear;
   bool HasBreakBefore() const {
-    return IsBlock() && NS_STYLE_CLEAR_NONE != mFlags.mBreakType;
+    return IsBlock() && StyleClear::None_ != BreakType();
   }
-  void SetBreakTypeBefore(uint8_t aBreakType) {
-    NS_ASSERTION(IsBlock(), "Only blocks have break-before");
-    NS_ASSERTION(aBreakType == NS_STYLE_CLEAR_NONE ||
-                 aBreakType == NS_STYLE_CLEAR_LEFT ||
-                 aBreakType == NS_STYLE_CLEAR_RIGHT ||
-                 aBreakType == NS_STYLE_CLEAR_BOTH,
-                 "Only float break types are allowed before a line");
-    mFlags.mBreakType = aBreakType;
+  void SetBreakTypeBefore(StyleClear aBreakType) {
+    MOZ_ASSERT(IsBlock(), "Only blocks have break-before");
+    MOZ_ASSERT(aBreakType == StyleClear::None_ ||
+               aBreakType == StyleClear::Left ||
+               aBreakType == StyleClear::Right ||
+               aBreakType == StyleClear::Both,
+               "Only float break types are allowed before a line");
+    mFlags.mBreakType = static_cast<int>(aBreakType);
   }
-  uint8_t GetBreakTypeBefore() const {
-    return IsBlock() ? mFlags.mBreakType : NS_STYLE_CLEAR_NONE;
+  StyleClear GetBreakTypeBefore() const {
+    return IsBlock() ? BreakType() : StyleClear::None_;
   }
 
   bool HasBreakAfter() const {
-    return !IsBlock() && NS_STYLE_CLEAR_NONE != mFlags.mBreakType;
+    return !IsBlock() && StyleClear::None_ != BreakType();
   }
-  void SetBreakTypeAfter(uint8_t aBreakType) {
-    NS_ASSERTION(!IsBlock(), "Only inlines have break-after");
-    NS_ASSERTION(aBreakType <= LINE_MAX_BREAK_TYPE, "bad break type");
-    mFlags.mBreakType = aBreakType;
+  void SetBreakTypeAfter(StyleClear aBreakType) {
+    MOZ_ASSERT(!IsBlock(), "Only inlines have break-after");
+    mFlags.mBreakType = static_cast<int>(aBreakType);
   }
   bool HasFloatBreakAfter() const {
-    return !IsBlock() && (NS_STYLE_CLEAR_LEFT == mFlags.mBreakType ||
-                          NS_STYLE_CLEAR_RIGHT == mFlags.mBreakType ||
-                          NS_STYLE_CLEAR_BOTH == mFlags.mBreakType);
+    return !IsBlock() &&
+           (StyleClear::Left == BreakType() ||
+            StyleClear::Right == BreakType() ||
+            StyleClear::Both == BreakType());
   }
-  uint8_t GetBreakTypeAfter() const {
-    return !IsBlock() ? mFlags.mBreakType : NS_STYLE_CLEAR_NONE;
+  StyleClear GetBreakTypeAfter() const {
+    return !IsBlock() ? BreakType() : StyleClear::None_;
   }
 
   // mCarriedOutBEndMargin value
   nsCollapsingMargin GetCarriedOutBEndMargin() const;
   // Returns true if the margin changed
   bool SetCarriedOutBEndMargin(nsCollapsingMargin aValue);
 
   // mFloats
@@ -567,16 +567,17 @@ public:
   // list).
   static bool RFindLineContaining(nsIFrame* aFrame,
                                     const nsLineList_iterator& aBegin,
                                     nsLineList_iterator& aEnd,
                                     nsIFrame* aLastFrameBeforeEnd,
                                     int32_t* aFrameIndexInLine);
 
 #ifdef DEBUG_FRAME_DUMP
+  const char* BreakTypeToString(StyleClear aBreakType) const;
   char* StateToString(char* aBuf, int32_t aBufSize) const;
 
   void List(FILE* out, int32_t aIndent, uint32_t aFlags = 0) const;
   void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const;
   nsIFrame* LastChild() const;
 #endif
 
 private:
@@ -658,17 +659,19 @@ public:
   struct FlagBits {
     uint32_t mDirty : 1;
     uint32_t mPreviousMarginDirty : 1;
     uint32_t mHasClearance : 1;
     uint32_t mBlock : 1;
     uint32_t mImpactedByFloat : 1;
     uint32_t mLineWrapped: 1;
     uint32_t mInvalidateTextRuns : 1;
-    uint32_t mResizeReflowOptimizationDisabled: 1;  // default 0 = means that the opt potentially applies to this line. 1 = never skip reflowing this line for a resize reflow
+    // default 0 = means that the opt potentially applies to this line.
+    // 1 = never skip reflowing this line for a resize reflow
+    uint32_t mResizeReflowOptimizationDisabled: 1;
     uint32_t mEmptyCacheValid: 1;
     uint32_t mEmptyCacheState: 1;
     // mHasBullet indicates that this is an inline line whose block's
     // bullet is adjacent to this line and non-empty.
     uint32_t mHasBullet : 1;
     // Indicates that this line *may* have a placeholder for a float
     // that was pushed to a later column or page.
     uint32_t mHadFloatPushed : 1;
@@ -694,21 +697,27 @@ public:
   struct ExtraInlineData : public ExtraData {
     explicit ExtraInlineData(const nsRect& aBounds) : ExtraData(aBounds) {
     }
     nsFloatCacheList mFloats;
   };
 
 protected:
   nscoord mAscent;           // see |SetAscent| / |GetAscent|
+  static_assert(sizeof(FlagBits) <= sizeof(uint32_t),
+                "size of FlagBits should not be larger than size of uint32_t");
   union {
     uint32_t mAllFlags;
     FlagBits mFlags;
   };
 
+  StyleClear BreakType() const {
+    return static_cast<StyleClear>(mFlags.mBreakType);
+  };
+
   union {
     ExtraData* mData;
     ExtraBlockData* mBlockData;
     ExtraInlineData* mInlineData;
   };
 
   void Cleanup();
   void MaybeFreeData();
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -444,17 +444,17 @@ public:
   void SetNormalValue();
   void SetAutoValue();
   void SetNoneValue();
   void SetIntValue(int32_t aInt, Unit aUnit);
   template<typename T,
            typename = typename std::enable_if<std::is_enum<T>::value>::type>
   void SetIntValue(T aInt, Unit aUnit)
   {
-    static_assert(mozilla::IsEnumFittingWithin<T, int32_t>::value,
+    static_assert(mozilla::EnumTypeFitsWithin<T, int32_t>::value,
                   "aValue must be an enum that fits within mValue.mInt");
     SetIntValue(static_cast<int32_t>(aInt), aUnit);
   }
   void SetCoordValue(nscoord aCoord);
   void SetPercentValue(float aPercent);
   void SetFloatValue(float aFloat);
   void SetColorValue(nscolor aColor);
   void SetCurrentColorValue();
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1053,23 +1053,23 @@ const KTableEntry nsCSSProps::kCaptionSi
   { eCSSKeyword_bottom,               NS_STYLE_CAPTION_SIDE_BOTTOM },
   { eCSSKeyword_left,                 NS_STYLE_CAPTION_SIDE_LEFT },
   { eCSSKeyword_top_outside,          NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE },
   { eCSSKeyword_bottom_outside,       NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE },
   { eCSSKeyword_UNKNOWN,              -1 }
 };
 
 KTableEntry nsCSSProps::kClearKTable[] = {
-  { eCSSKeyword_none,         NS_STYLE_CLEAR_NONE },
-  { eCSSKeyword_left,         NS_STYLE_CLEAR_LEFT },
-  { eCSSKeyword_right,        NS_STYLE_CLEAR_RIGHT },
-  { eCSSKeyword_inline_start, NS_STYLE_CLEAR_INLINE_START },
-  { eCSSKeyword_inline_end,   NS_STYLE_CLEAR_INLINE_END },
-  { eCSSKeyword_both,         NS_STYLE_CLEAR_BOTH },
-  { eCSSKeyword_UNKNOWN,      -1 }
+  { eCSSKeyword_none, StyleClear::None_ },
+  { eCSSKeyword_left, StyleClear::Left },
+  { eCSSKeyword_right, StyleClear::Right },
+  { eCSSKeyword_inline_start, StyleClear::InlineStart },
+  { eCSSKeyword_inline_end, StyleClear::InlineEnd },
+  { eCSSKeyword_both, StyleClear::Both },
+  { eCSSKeyword_UNKNOWN, -1 }
 };
 
 // See also kContextPatternKTable for SVG paint-specific values
 const KTableEntry nsCSSProps::kColorKTable[] = {
   { eCSSKeyword_activeborder, LookAndFeel::eColorID_activeborder },
   { eCSSKeyword_activecaption, LookAndFeel::eColorID_activecaption },
   { eCSSKeyword_appworkspace, LookAndFeel::eColorID_appworkspace },
   { eCSSKeyword_background, LookAndFeel::eColorID_background },
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -14,16 +14,17 @@
 #include <limits>
 #include <type_traits>
 #include "nsString.h"
 #include "nsCSSPropertyID.h"
 #include "nsStyleStructFwd.h"
 #include "nsCSSKeywords.h"
 #include "mozilla/CSSEnabledState.h"
 #include "mozilla/UseCounter.h"
+#include "mozilla/EnumTypeTraits.h"
 
 // Length of the "--" prefix on custom names (such as custom property names,
 // and, in the future, custom media query names).
 #define CSS_CUSTOM_NAME_PREFIX_LENGTH 2
 
 // Flags for ParseVariant method
 #define VARIANT_KEYWORD         0x000001  // K
 #define VARIANT_LENGTH          0x000002  // L
@@ -322,34 +323,16 @@ enum nsStyleAnimType {
 
   // discrete values
   eStyleAnimType_Discrete,
 
   // property not animatable
   eStyleAnimType_None
 };
 
-namespace mozilla {
-
-// Type trait that determines whether the integral or enum type Type can fit
-// within the integral type Storage without loss.
-template<typename T, typename Storage>
-struct IsEnumFittingWithin
-  : IntegralConstant<
-      bool,
-      std::is_integral<Storage>::value &&
-      std::numeric_limits<typename std::underlying_type<T>::type>::min() >=
-        std::numeric_limits<Storage>::min() &&
-      std::numeric_limits<typename std::underlying_type<T>::type>::max() <=
-        std::numeric_limits<Storage>::max()
-    >
-{};
-
-} // namespace mozilla
-
 class nsCSSProps {
 public:
   typedef mozilla::CSSEnabledState EnabledState;
 
   struct KTableEntry
   {
     // KTableEntry objects can be initialized either with an int16_t value
     // or a value of an enumeration type that can fit within an int16_t.
@@ -361,17 +344,17 @@ public:
     }
 
     template<typename T,
              typename = typename std::enable_if<std::is_enum<T>::value>::type>
     KTableEntry(nsCSSKeyword aKeyword, T aValue)
       : mKeyword(aKeyword)
       , mValue(static_cast<int16_t>(aValue))
     {
-      static_assert(mozilla::IsEnumFittingWithin<T, int16_t>::value,
+      static_assert(mozilla::EnumTypeFitsWithin<T, int16_t>::value,
                     "aValue must be an enum that fits within mValue");
     }
 
     nsCSSKeyword mKeyword;
     int16_t mValue;
   };
 
   static void AddRefTable(void);
@@ -447,29 +430,29 @@ public:
   // Return |eCSSKeyword_UNKNOWN| if not found.
   static nsCSSKeyword ValueToKeywordEnum(int32_t aValue,
                                          const KTableEntry aTable[]);
   template<typename T,
            typename = typename std::enable_if<std::is_enum<T>::value>::type>
   static nsCSSKeyword ValueToKeywordEnum(T aValue,
                                          const KTableEntry aTable[])
   {
-    static_assert(mozilla::IsEnumFittingWithin<T, int16_t>::value,
+    static_assert(mozilla::EnumTypeFitsWithin<T, int16_t>::value,
                   "aValue must be an enum that fits within KTableEntry::mValue");
     return ValueToKeywordEnum(static_cast<int16_t>(aValue), aTable);
   }
   // Ditto but as a string, return "" when not found.
   static const nsAFlatCString& ValueToKeyword(int32_t aValue,
                                               const KTableEntry aTable[]);
   template<typename T,
            typename = typename std::enable_if<std::is_enum<T>::value>::type>
   static const nsAFlatCString& ValueToKeyword(T aValue,
                                               const KTableEntry aTable[])
   {
-    static_assert(mozilla::IsEnumFittingWithin<T, int16_t>::value,
+    static_assert(mozilla::EnumTypeFitsWithin<T, int16_t>::value,
                   "aValue must be an enum that fits within KTableEntry::mValue");
     return ValueToKeyword(static_cast<int16_t>(aValue), aTable);
   }
 
   static const nsStyleStructID kSIDTable[eCSSProperty_COUNT_no_shorthands];
   static const KTableEntry* const kKeywordTableTable[eCSSProperty_COUNT_no_shorthands];
   static const nsStyleAnimType kAnimTypeTable[eCSSProperty_COUNT_no_shorthands];
   static const ptrdiff_t
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -753,17 +753,17 @@ private:
   void DoReset();
 
 public:
   void SetIntValue(int32_t aValue, nsCSSUnit aUnit);
   template<typename T,
            typename = typename std::enable_if<std::is_enum<T>::value>::type>
   void SetIntValue(T aValue, nsCSSUnit aUnit)
   {
-    static_assert(mozilla::IsEnumFittingWithin<T, int32_t>::value,
+    static_assert(mozilla::EnumTypeFitsWithin<T, int32_t>::value,
                   "aValue must be an enum that fits within mValue.mInt");
     SetIntValue(static_cast<int32_t>(aValue), aUnit);
   }
   void SetPercentValue(float aValue);
   void SetFloatValue(float aValue, nsCSSUnit aUnit);
   void SetStringValue(const nsString& aValue, nsCSSUnit aUnit);
   void SetColorValue(nscolor aValue);
   void SetIntegerColorValue(nscolor aValue, nsCSSUnit aUnit);
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -1337,16 +1337,17 @@ struct SetEnumValueHelper
   }
 
   DEFINE_ENUM_CLASS_SETTER(StyleBoxAlign, Stretch, End)
   DEFINE_ENUM_CLASS_SETTER(StyleBoxDecorationBreak, Slice, Clone)
   DEFINE_ENUM_CLASS_SETTER(StyleBoxDirection, Normal, Reverse)
   DEFINE_ENUM_CLASS_SETTER(StyleBoxOrient, Horizontal, Vertical)
   DEFINE_ENUM_CLASS_SETTER(StyleBoxPack, Start, Justify)
   DEFINE_ENUM_CLASS_SETTER(StyleBoxSizing, Content, Border)
+  DEFINE_ENUM_CLASS_SETTER(StyleClear, None_, Both)
   DEFINE_ENUM_CLASS_SETTER(StyleFillRule, Nonzero, Evenodd)
   DEFINE_ENUM_CLASS_SETTER(StyleFloat, None_, InlineEnd)
   DEFINE_ENUM_CLASS_SETTER(StyleFloatEdge, ContentBox, MarginBox)
   DEFINE_ENUM_CLASS_SETTER(StyleUserFocus, None_, SelectMenu)
   DEFINE_ENUM_CLASS_SETTER(StyleUserSelect, None_, MozText)
 #ifdef MOZ_XUL
   DEFINE_ENUM_CLASS_SETTER(StyleDisplay, None_, Popup)
 #else
@@ -6039,17 +6040,17 @@ nsRuleNode::ComputeDisplayData(void* aSt
     // an aStartStruct for some other elements.
     conditions.SetUncacheable();
   }
 
   // clear: enum, inherit, initial
   SetValue(*aRuleData->ValueForClear(), display->mBreakType, conditions,
            SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL,
            parentDisplay->mBreakType,
-           NS_STYLE_CLEAR_NONE);
+           StyleClear::None_);
 
   // temp fix for bug 24000
   // Map 'auto' and 'avoid' to false, and 'always', 'left', and
   // 'right' to true.
   // "A conforming user agent may interpret the values 'left' and
   // 'right' as 'always'." - CSS2.1, section 13.3.1
   const nsCSSValue* breakBeforeValue = aRuleData->ValueForPageBreakBefore();
   if (eCSSUnit_Enumerated == breakBeforeValue->GetUnit()) {
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -103,16 +103,30 @@ enum class StyleBoxSizing : uint8_t {
   Border
 };
 
 // box-shadow
 enum class StyleBoxShadowType : uint8_t {
   Inset,
 };
 
+// clear
+enum class StyleClear : uint8_t {
+  None_ = 0,
+  Left,
+  Right,
+  InlineStart,
+  InlineEnd,
+  Both,
+  // StyleClear::Line can be added to one of the other values in layout
+  // so it needs to use a bit value that none of the other values can have.
+  Line = 8,
+  Max = 13  // Max = (Both | Line)
+};
+
 // clip-path geometry box
 enum class StyleClipPathGeometryBox : uint8_t {
   NoBox,
   Content,
   Padding,
   Border,
   Margin,
   Fill,
@@ -398,28 +412,16 @@ enum class FillMode : uint32_t;
 #define NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH    0
 #define NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT     1
 #define NS_STYLE_BORDER_IMAGE_REPEAT_ROUND      2
 #define NS_STYLE_BORDER_IMAGE_REPEAT_SPACE      3
 
 #define NS_STYLE_BORDER_IMAGE_SLICE_NOFILL      0
 #define NS_STYLE_BORDER_IMAGE_SLICE_FILL        1
 
-// See nsStyleDisplay
-#define NS_STYLE_CLEAR_NONE                     0
-#define NS_STYLE_CLEAR_LEFT                     1
-#define NS_STYLE_CLEAR_RIGHT                    2
-#define NS_STYLE_CLEAR_INLINE_START             3
-#define NS_STYLE_CLEAR_INLINE_END               4
-#define NS_STYLE_CLEAR_BOTH                     5
-#define NS_STYLE_CLEAR_LINE                     8
-// @note NS_STYLE_CLEAR_LINE can be added to one of the other values in layout
-// so it needs to use a bit value that none of the other values can have.
-#define NS_STYLE_CLEAR_MAX (NS_STYLE_CLEAR_LINE | NS_STYLE_CLEAR_BOTH)
-
 // See nsStyleContent
 #define NS_STYLE_CONTENT_OPEN_QUOTE             0
 #define NS_STYLE_CONTENT_CLOSE_QUOTE            1
 #define NS_STYLE_CONTENT_NO_OPEN_QUOTE          2
 #define NS_STYLE_CONTENT_NO_CLOSE_QUOTE         3
 #define NS_STYLE_CONTENT_ALT_CONTENT            4
 
 // See nsStyleColor
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2983,17 +2983,17 @@ StyleAnimation::operator==(const StyleAn
 nsStyleDisplay::nsStyleDisplay(StyleStructContext aContext)
   : mDisplay(StyleDisplay::Inline)
   , mOriginalDisplay(StyleDisplay::Inline)
   , mContain(NS_STYLE_CONTAIN_NONE)
   , mAppearance(NS_THEME_NONE)
   , mPosition(NS_STYLE_POSITION_STATIC)
   , mFloat(StyleFloat::None_)
   , mOriginalFloat(StyleFloat::None_)
-  , mBreakType(NS_STYLE_CLEAR_NONE)
+  , mBreakType(StyleClear::None_)
   , mBreakInside(NS_STYLE_PAGE_BREAK_AUTO)
   , mBreakBefore(false)
   , mBreakAfter(false)
   , mOverflowX(NS_STYLE_OVERFLOW_VISIBLE)
   , mOverflowY(NS_STYLE_OVERFLOW_VISIBLE)
   , mOverflowClipBox(NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX)
   , mResize(NS_STYLE_RESIZE_NONE)
   , mOrient(NS_STYLE_ORIENT_INLINE)
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2835,17 +2835,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   uint8_t mAppearance;          // [reset]
   uint8_t mPosition;            // [reset] see nsStyleConsts.h
 
   // [reset] See StyleFloat in nsStyleConsts.h.
   mozilla::StyleFloat mFloat;
   // [reset] Save mFloat for position:absolute/fixed; otherwise equal to mFloat.
   mozilla::StyleFloat mOriginalFloat;
 
-  uint8_t mBreakType;           // [reset] see nsStyleConsts.h NS_STYLE_CLEAR_*
+  mozilla::StyleClear mBreakType;  // [reset]
   uint8_t mBreakInside;         // [reset] NS_STYLE_PAGE_BREAK_AUTO/AVOID
   bool mBreakBefore;    // [reset]
   bool mBreakAfter;     // [reset]
   uint8_t mOverflowX;           // [reset] see nsStyleConsts.h
   uint8_t mOverflowY;           // [reset] see nsStyleConsts.h
   uint8_t mOverflowClipBox;     // [reset] see nsStyleConsts.h
   uint8_t mResize;              // [reset] see nsStyleConsts.h
   uint8_t mOrient;              // [reset] see nsStyleConsts.h
@@ -3089,17 +3089,17 @@ private:
   inline bool HasFixedPosContainingBlockStyleInternal(
                 StyleContextLike* aStyleContext) const;
 
 public:
   // Return the 'float' and 'clear' properties, with inline-{start,end} values
   // resolved to {left,right} according to the given writing mode. These are
   // defined in WritingModes.h.
   inline mozilla::StyleFloat PhysicalFloats(mozilla::WritingMode aWM) const;
-  inline uint8_t PhysicalBreakType(mozilla::WritingMode aWM) const;
+  inline mozilla::StyleClear PhysicalBreakType(mozilla::WritingMode aWM) const;
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleTable
 {
   explicit nsStyleTable(StyleStructContext aContext);
   nsStyleTable(const nsStyleTable& aOther);
   ~nsStyleTable();
 
new file mode 100644
--- /dev/null
+++ b/mfbt/EnumTypeTraits.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Type traits for enums. */
+
+#ifndef mozilla_EnumTypeTraits_h
+#define mozilla_EnumTypeTraits_h
+
+#include <type_traits>
+
+namespace mozilla {
+
+namespace detail {
+
+template<size_t EnumSize, bool EnumSigned, size_t StorageSize, bool StorageSigned>
+struct EnumFitsWithinHelper;
+
+// Signed enum, signed storage.
+template<size_t EnumSize, size_t StorageSize>
+struct EnumFitsWithinHelper<EnumSize, true, StorageSize, true>
+  : public std::integral_constant<bool, (EnumSize <= StorageSize)>
+{};
+
+// Signed enum, unsigned storage.
+template<size_t EnumSize, size_t StorageSize>
+struct EnumFitsWithinHelper<EnumSize, true, StorageSize, false>
+  : public std::integral_constant<bool, false>
+{};
+
+// Unsigned enum, signed storage.
+template<size_t EnumSize, size_t StorageSize>
+struct EnumFitsWithinHelper<EnumSize, false, StorageSize, true>
+  : public std::integral_constant<bool, (EnumSize * 2 <= StorageSize)>
+{};
+
+// Unsigned enum, unsigned storage.
+template<size_t EnumSize, size_t StorageSize>
+struct EnumFitsWithinHelper<EnumSize, false, StorageSize, false>
+  : public std::integral_constant<bool, (EnumSize <= StorageSize)>
+{};
+
+} // namespace detail
+
+/*
+ * Type trait that determines whether the enum type T can fit within the
+ * integral type Storage without data loss. This trait should be used with
+ * caution with an enum type whose underlying type has not been explicitly
+ * specified: for such enums, the C++ implementation is free to choose a type
+ * no smaller than int whose range encompasses all possible values of the enum.
+ * So for an enum with only small non-negative values, the underlying type may
+ * be either int or unsigned int, depending on the whims of the implementation.
+ */
+template<typename T, typename Storage>
+struct EnumTypeFitsWithin
+  : public detail::EnumFitsWithinHelper<
+      sizeof(T),
+      std::is_signed<typename std::underlying_type<T>::type>::value,
+      sizeof(Storage),
+      std::is_signed<Storage>::value
+    >
+{
+  static_assert(std::is_enum<T>::value, "must provide an enum type");
+  static_assert(std::is_integral<Storage>::value, "must provide an integral type");
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_EnumTypeTraits_h */
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -34,16 +34,17 @@ EXPORTS.mozilla = [
     'DebugOnly.h',
     'decimal/Decimal.h',
     'double-conversion/double-conversion.h',
     'double-conversion/utils.h',
     'EndianUtils.h',
     'EnumeratedArray.h',
     'EnumeratedRange.h',
     'EnumSet.h',
+    'EnumTypeTraits.h',
     'FastBernoulliTrial.h',
     'FloatingPoint.h',
     'Function.h',
     'GuardObjects.h',
     'HashFunctions.h',
     'IndexSequence.h',
     'InitializerList.h',
     'IntegerPrintfMacros.h',
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/TestEnumTypeTraits.cpp
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/IntegerTypeTraits.h"
+#include "mozilla/EnumTypeTraits.h"
+
+using namespace mozilla;
+
+/* Feature check for EnumTypeFitsWithin. */
+
+#define MAKE_FIXED_EMUM_FOR_TYPE(IntType)                               \
+  enum FixedEnumFor_##IntType : IntType {                               \
+    A_##IntType,                                                        \
+    B_##IntType,                                                        \
+    C_##IntType,                                                        \
+  };
+
+template<typename EnumType, typename IntType>
+static void
+TestShouldFit()
+{
+  static_assert(EnumTypeFitsWithin<EnumType, IntType>::value,
+                "Should fit within exact/promoted integral type");
+}
+
+template<typename EnumType, typename IntType>
+static void
+TestShouldNotFit()
+{
+  static_assert(!EnumTypeFitsWithin<EnumType, IntType>::value,
+                "Should not fit within");
+}
+
+int
+main()
+{
+  // check for int8_t
+  MAKE_FIXED_EMUM_FOR_TYPE(int8_t);
+  TestShouldFit<FixedEnumFor_int8_t, int8_t>();
+  TestShouldFit<FixedEnumFor_int8_t, int16_t>();
+  TestShouldFit<FixedEnumFor_int8_t, int32_t>();
+  TestShouldFit<FixedEnumFor_int8_t, int64_t>();
+
+  TestShouldNotFit<FixedEnumFor_int8_t, uint8_t>();
+  TestShouldNotFit<FixedEnumFor_int8_t, uint16_t>();
+  TestShouldNotFit<FixedEnumFor_int8_t, uint32_t>();
+  TestShouldNotFit<FixedEnumFor_int8_t, uint64_t>();
+
+  // check for uint8_t
+  MAKE_FIXED_EMUM_FOR_TYPE(uint8_t);
+  TestShouldFit<FixedEnumFor_uint8_t, uint8_t>();
+  TestShouldFit<FixedEnumFor_uint8_t, uint16_t>();
+  TestShouldFit<FixedEnumFor_uint8_t, uint32_t>();
+  TestShouldFit<FixedEnumFor_uint8_t, uint64_t>();
+
+  TestShouldNotFit<FixedEnumFor_uint8_t, int8_t>();
+  TestShouldFit<FixedEnumFor_uint8_t, int16_t>();
+  TestShouldFit<FixedEnumFor_uint8_t, int32_t>();
+  TestShouldFit<FixedEnumFor_uint8_t, int64_t>();
+
+  // check for int16_t
+  MAKE_FIXED_EMUM_FOR_TYPE(int16_t);
+  TestShouldNotFit<FixedEnumFor_int16_t, int8_t>();
+  TestShouldFit<FixedEnumFor_int16_t, int16_t>();
+  TestShouldFit<FixedEnumFor_int16_t, int32_t>();
+  TestShouldFit<FixedEnumFor_int16_t, int64_t>();
+
+  TestShouldNotFit<FixedEnumFor_int16_t, uint8_t>();
+  TestShouldNotFit<FixedEnumFor_int16_t, uint16_t>();
+  TestShouldNotFit<FixedEnumFor_int16_t, uint32_t>();
+  TestShouldNotFit<FixedEnumFor_int16_t, uint64_t>();
+
+  // check for uint16_t
+  MAKE_FIXED_EMUM_FOR_TYPE(uint16_t);
+  TestShouldNotFit<FixedEnumFor_uint16_t, uint8_t>();
+  TestShouldFit<FixedEnumFor_uint16_t, uint16_t>();
+  TestShouldFit<FixedEnumFor_uint16_t, uint32_t>();
+  TestShouldFit<FixedEnumFor_uint16_t, uint64_t>();
+
+  TestShouldNotFit<FixedEnumFor_uint16_t, int8_t>();
+  TestShouldNotFit<FixedEnumFor_uint16_t, int16_t>();
+  TestShouldFit<FixedEnumFor_uint16_t, int32_t>();
+  TestShouldFit<FixedEnumFor_uint16_t, int64_t>();
+
+  // check for int32_t
+  MAKE_FIXED_EMUM_FOR_TYPE(int32_t);
+  TestShouldNotFit<FixedEnumFor_int32_t, int8_t>();
+  TestShouldNotFit<FixedEnumFor_int32_t, int16_t>();
+  TestShouldFit<FixedEnumFor_int32_t, int32_t>();
+  TestShouldFit<FixedEnumFor_int32_t, int64_t>();
+
+  TestShouldNotFit<FixedEnumFor_int32_t, uint8_t>();
+  TestShouldNotFit<FixedEnumFor_int32_t, uint16_t>();
+  TestShouldNotFit<FixedEnumFor_int32_t, uint32_t>();
+  TestShouldNotFit<FixedEnumFor_int32_t, uint64_t>();
+
+  // check for uint32_t
+  MAKE_FIXED_EMUM_FOR_TYPE(uint32_t);
+  TestShouldNotFit<FixedEnumFor_uint32_t, uint8_t>();
+  TestShouldNotFit<FixedEnumFor_uint32_t, uint16_t>();
+  TestShouldFit<FixedEnumFor_uint32_t, uint32_t>();
+  TestShouldFit<FixedEnumFor_uint32_t, uint64_t>();
+
+  TestShouldNotFit<FixedEnumFor_uint32_t, int8_t>();
+  TestShouldNotFit<FixedEnumFor_uint32_t, int16_t>();
+  TestShouldNotFit<FixedEnumFor_uint32_t, int32_t>();
+  TestShouldFit<FixedEnumFor_uint32_t, int64_t>();
+
+  // check for int64_t
+  MAKE_FIXED_EMUM_FOR_TYPE(int64_t);
+  TestShouldNotFit<FixedEnumFor_int64_t, int8_t>();
+  TestShouldNotFit<FixedEnumFor_int64_t, int16_t>();
+  TestShouldNotFit<FixedEnumFor_int64_t, int32_t>();
+  TestShouldFit<FixedEnumFor_int64_t, int64_t>();
+
+  TestShouldNotFit<FixedEnumFor_int64_t, uint8_t>();
+  TestShouldNotFit<FixedEnumFor_int64_t, uint16_t>();
+  TestShouldNotFit<FixedEnumFor_int64_t, uint32_t>();
+  TestShouldNotFit<FixedEnumFor_int64_t, uint64_t>();
+
+  // check for uint64_t
+  MAKE_FIXED_EMUM_FOR_TYPE(uint64_t);
+  TestShouldNotFit<FixedEnumFor_uint64_t, uint8_t>();
+  TestShouldNotFit<FixedEnumFor_uint64_t, uint16_t>();
+  TestShouldNotFit<FixedEnumFor_uint64_t, uint32_t>();
+  TestShouldFit<FixedEnumFor_uint64_t, uint64_t>();
+
+  TestShouldNotFit<FixedEnumFor_uint64_t, int8_t>();
+  TestShouldNotFit<FixedEnumFor_uint64_t, int16_t>();
+  TestShouldNotFit<FixedEnumFor_uint64_t, int32_t>();
+  TestShouldNotFit<FixedEnumFor_uint64_t, int64_t>();
+
+  return 0;
+}
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -12,16 +12,17 @@ CppUnitTests([
     'TestBufferList',
     'TestCasting',
     'TestCeilingFloor',
     'TestCheckedInt',
     'TestCountPopulation',
     'TestCountZeroes',
     'TestEndian',
     'TestEnumSet',
+    'TestEnumTypeTraits',
     'TestFastBernoulliTrial',
     'TestFloatingPoint',
     'TestFunction',
     'TestInitializerList',
     'TestIntegerPrintfMacros',
     'TestIntegerRange',
     'TestJSONWriter',
     'TestLinkedList',
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -2568,17 +2568,19 @@ public abstract class GeckoApp
     public void notifyWakeLockChanged(String topic, String state) {
         PowerManager.WakeLock wl = mWakeLocks.get(topic);
         if (state.equals("locked-foreground") && wl == null) {
             PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
 
             if (CPU.equals(topic)) {
               wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, topic);
             } else if (SCREEN.equals(topic)) {
-              wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, topic);
+              // ON_AFTER_RELEASE is set, the user activity timer will be reset when the
+              // WakeLock is released, causing the illumination to remain on a bit longer.
+              wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, topic);
             }
 
             if (wl != null) {
               wl.acquire();
               mWakeLocks.put(topic, wl);
             }
         } else if (!state.equals("locked-foreground") && wl != null) {
             wl.release();
--- a/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java
@@ -63,16 +63,22 @@ public class LauncherActivity extends Ac
     }
 
     /**
      * Launch the browser activity.
      */
     private void dispatchNormalIntent() {
         Intent intent = new Intent(getIntent());
         intent.setClassName(getApplicationContext(), AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
+
+        // Explicitly remove the new task and clear task flags (Our browser activity is a single
+        // task activity and we never want to start a second task here). See bug 1280112.
+        intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
         startActivity(intent);
     }
 
     private void dispatchCustomTabsIntent() {
         Intent intent = new Intent(getIntent());
         intent.setClassName(getApplicationContext(), CustomTabsActivity.class.getName());
         startActivity(intent);
     }
--- a/mobile/android/base/java/org/mozilla/gecko/media/Sample.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/Sample.java
@@ -34,17 +34,17 @@ public final class Sample implements Par
 
     public static Sample createDummyWithInfo(BufferInfo info) {
         BufferInfo dummyInfo = new BufferInfo();
         dummyInfo.set(0, 0, info.presentationTimeUs, info.flags);
         return new Sample(null, dummyInfo);
     }
 
     public boolean isDummy() {
-        return bytes == null && info.size == 0;
+        return !isEOS() && bytes == null && info.size == 0;
     }
 
     public boolean isEOS() {
         return (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
     }
 
     public static final Creator<Sample> CREATOR = new Creator<Sample>() {
         @Override
--- a/python/mozlint/mozlint/vcs.py
+++ b/python/mozlint/mozlint/vcs.py
@@ -18,17 +18,17 @@ class VCSFiles(object):
 
         # First check if we're in an hg repo, if not try git
         commands = (
             ['hg', 'root'],
             ['git', 'rev-parse', '--show-toplevel'],
         )
 
         for cmd in commands:
-            proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+            proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
             output = proc.communicate()[0].strip()
 
             if proc.returncode == 0:
                 self._vcs = cmd[0]
                 self._root = output
                 return self._root
 
     @property
--- a/services/sync/modules/engines/bookmarks.js
+++ b/services/sync/modules/engines/bookmarks.js
@@ -22,31 +22,41 @@ Cu.import("resource://services-sync/book
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/PlacesBackups.jsm");
 
 const ANNOS_TO_TRACK = [BookmarkAnnos.DESCRIPTION_ANNO, BookmarkAnnos.SIDEBAR_ANNO,
                         PlacesUtils.LMANNO_FEEDURI, PlacesUtils.LMANNO_SITEURI];
 
 const SERVICE_NOT_SUPPORTED = "Service not supported on this platform";
 const FOLDER_SORTINDEX = 1000000;
-const { SOURCE_SYNC } = Ci.nsINavBookmarksService;
+const {
+  SOURCE_SYNC,
+  SOURCE_IMPORT,
+  SOURCE_IMPORT_REPLACE,
+} = Ci.nsINavBookmarksService;
 
 // Maps Sync record property names to `PlacesSyncUtils` bookmark properties.
 const RECORD_PROPS_TO_BOOKMARK_PROPS = {
   title: "title",
   bmkUri: "url",
   tags: "tags",
   keyword: "keyword",
   description: "description",
   loadInSidebar: "loadInSidebar",
   queryId: "query",
   siteUri: "site",
   feedUri: "feed",
 };
 
+// The tracker ignores changes made by bookmark import and restore, and
+// changes made by Sync. We don't need to exclude `SOURCE_IMPORT`, but both
+// import and restore fire `bookmarks-restore-*` observer notifications, and
+// the tracker doesn't currently distinguish between the two.
+const IGNORED_SOURCES = [SOURCE_SYNC, SOURCE_IMPORT, SOURCE_IMPORT_REPLACE];
+
 this.PlacesItem = function PlacesItem(collection, id, type) {
   CryptoWrapper.call(this, collection, id);
   this.type = type || "item";
 }
 PlacesItem.prototype = {
   decrypt: function PlacesItem_decrypt(keyBundle) {
     // Do the normal CryptoWrapper decrypt, but change types before returning
     let clear = CryptoWrapper.prototype.decrypt.call(this, keyBundle);
@@ -372,19 +382,17 @@ BookmarksEngine.prototype = {
     this._store._childrenToOrder = {};
   },
 
   _processIncoming: function (newitems) {
     try {
       SyncEngine.prototype._processIncoming.call(this, newitems);
     } finally {
       // Reorder children.
-      this._tracker.ignoreAll = true;
       this._store._orderChildren();
-      this._tracker.ignoreAll = false;
       delete this._store._childrenToOrder;
     }
   },
 
   _syncFinish: function _syncFinish() {
     SyncEngine.prototype._syncFinish.call(this);
     this._tracker._ensureMobileQuery();
   },
@@ -900,16 +908,26 @@ function BookmarksTracker(name, engine) 
   this._batchSawScoreIncrement = false;
   Tracker.call(this, name, engine);
 
   Svc.Obs.add("places-shutdown", this);
 }
 BookmarksTracker.prototype = {
   __proto__: Tracker.prototype,
 
+  //`_ignore` checks the change source for each observer notification, so we
+  // don't want to let the engine ignore all changes during a sync.
+  get ignoreAll() {
+    return false;
+  },
+
+  // Define an empty setter so that the engine doesn't throw a `TypeError`
+  // setting a read-only property.
+  set ignoreAll(value) {},
+
   startTracking: function() {
     PlacesUtils.bookmarks.addObserver(this, true);
     Svc.Obs.add("bookmarks-restore-begin", this);
     Svc.Obs.add("bookmarks-restore-success", this);
     Svc.Obs.add("bookmarks-restore-failed", this);
   },
 
   stopTracking: function() {
@@ -920,30 +938,27 @@ BookmarksTracker.prototype = {
   },
 
   observe: function observe(subject, topic, data) {
     Tracker.prototype.observe.call(this, subject, topic, data);
 
     switch (topic) {
       case "bookmarks-restore-begin":
         this._log.debug("Ignoring changes from importing bookmarks.");
-        this.ignoreAll = true;
         break;
       case "bookmarks-restore-success":
         this._log.debug("Tracking all items on successful import.");
-        this.ignoreAll = false;
 
         this._log.debug("Restore succeeded: wiping server and other clients.");
         this.engine.service.resetClient([this.name]);
         this.engine.service.wipeServer([this.name]);
         this.engine.service.clientsEngine.sendCommand("wipeEngine", [this.name]);
         break;
       case "bookmarks-restore-failed":
         this._log.debug("Tracking all items on failed import.");
-        this.ignoreAll = false;
         break;
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsINavBookmarkObserver,
     Ci.nsINavBookmarkObserver_MOZILLA_1_9_1_ADDITIONS,
     Ci.nsISupportsWeakReference
@@ -973,21 +988,25 @@ BookmarksTracker.prototype = {
 
   /**
    * Determine if a change should be ignored.
    *
    * @param itemId
    *        Item under consideration to ignore
    * @param folder (optional)
    *        Folder of the item being changed
+   * @param guid
+   *        Places GUID of the item being changed
+   * @param source
+   *        A change source constant from `nsINavBookmarksService::SOURCE_*`.
    */
-  _ignore: function BMT__ignore(itemId, folder, guid) {
-    // Ignore unconditionally if the engine tells us to.
-    if (this.ignoreAll)
+  _ignore: function BMT__ignore(itemId, folder, guid, source) {
+    if (IGNORED_SOURCES.includes(source)) {
       return true;
+    }
 
     // Get the folder id if we weren't given one.
     if (folder == null) {
       try {
         folder = PlacesUtils.bookmarks.getFolderIdForItem(itemId);
       } catch (ex) {
         this._log.debug("getFolderIdForItem(" + itemId +
                         ") threw; calling _ensureMobileQuery.");
@@ -1013,28 +1032,29 @@ BookmarksTracker.prototype = {
       return true;
     }
 
     return false;
   },
 
   onItemAdded: function BMT_onItemAdded(itemId, folder, index,
                                         itemType, uri, title, dateAdded,
-                                        guid, parentGuid) {
-    if (this._ignore(itemId, folder, guid))
+                                        guid, parentGuid, source) {
+    if (this._ignore(itemId, folder, guid, source)) {
       return;
+    }
 
     this._log.trace("onItemAdded: " + itemId);
     this._add(itemId, guid);
     this._add(folder, parentGuid);
   },
 
   onItemRemoved: function (itemId, parentId, index, type, uri,
-                           guid, parentGuid) {
-    if (this._ignore(itemId, parentId, guid)) {
+                           guid, parentGuid, source) {
+    if (this._ignore(itemId, parentId, guid, source)) {
       return;
     }
 
     this._log.trace("onItemRemoved: " + itemId);
     this._add(itemId, guid);
     this._add(parentId, parentGuid);
   },
 
@@ -1044,19 +1064,16 @@ BookmarksTracker.prototype = {
         id => PlacesUtils.annotations.getItemAnnotation(id, BookmarkAnnos.ORGANIZERQUERY_ANNO) == val
       );
 
     // Don't continue if the Library isn't ready
     let all = find(BookmarkAnnos.ALLBOOKMARKS_ANNO);
     if (all.length == 0)
       return;
 
-    // Disable handling of notifications while changing the mobile query
-    this.ignoreAll = true;
-
     let mobile = find(BookmarkAnnos.MOBILE_ANNO);
     let queryURI = Utils.makeURI("place:folder=" + BookmarkSpecialIds.mobile);
     let title = Str.sync.get("mobile.label");
 
     // Don't add OR remove the mobile bookmarks if there's nothing.
     if (PlacesUtils.bookmarks.getIdForItemAt(BookmarkSpecialIds.mobile, 0) == -1) {
       if (mobile.length != 0)
         PlacesUtils.bookmarks.removeItem(mobile[0], SOURCE_SYNC);
@@ -1068,52 +1085,49 @@ BookmarksTracker.prototype = {
                                   PlacesUtils.annotations.EXPIRE_NEVER, SOURCE_SYNC);
       PlacesUtils.annotations.setItemAnnotation(query, BookmarkAnnos.EXCLUDEBACKUP_ANNO, 1, 0,
                                   PlacesUtils.annotations.EXPIRE_NEVER, SOURCE_SYNC);
     }
     // Make sure the existing title is correct
     else if (PlacesUtils.bookmarks.getItemTitle(mobile[0]) != title) {
       PlacesUtils.bookmarks.setItemTitle(mobile[0], title, SOURCE_SYNC);
     }
-
-    this.ignoreAll = false;
   },
 
   // This method is oddly structured, but the idea is to return as quickly as
   // possible -- this handler gets called *every time* a bookmark changes, for
   // *each change*.
   onItemChanged: function BMT_onItemChanged(itemId, property, isAnno, value,
                                             lastModified, itemType, parentId,
-                                            guid, parentGuid) {
-    // Quicker checks first.
-    if (this.ignoreAll)
-      return;
-
+                                            guid, parentGuid, source) {
     if (isAnno && (ANNOS_TO_TRACK.indexOf(property) == -1))
       // Ignore annotations except for the ones that we sync.
       return;
 
     // Ignore favicon changes to avoid unnecessary churn.
     if (property == "favicon")
       return;
 
-    if (this._ignore(itemId, parentId, guid))
+    if (this._ignore(itemId, parentId, guid, source)) {
       return;
+    }
 
     this._log.trace("onItemChanged: " + itemId +
                     (", " + property + (isAnno? " (anno)" : "")) +
                     (value ? (" = \"" + value + "\"") : ""));
     this._add(itemId, guid);
   },
 
   onItemMoved: function BMT_onItemMoved(itemId, oldParent, oldIndex,
                                         newParent, newIndex, itemType,
-                                        guid, oldParentGuid, newParentGuid) {
-    if (this._ignore(itemId, newParent, guid))
+                                        guid, oldParentGuid, newParentGuid,
+                                        source) {
+    if (this._ignore(itemId, newParent, guid, source)) {
       return;
+    }
 
     this._log.trace("onItemMoved: " + itemId);
     this._add(oldParent, oldParentGuid);
     if (oldParent != newParent) {
       this._add(itemId, guid);
       this._add(newParent, newParentGuid);
     }
 
--- a/services/sync/modules/engines/prefs.js
+++ b/services/sync/modules/engines/prefs.js
@@ -105,51 +105,63 @@ PrefStore.prototype = {
       if (this._isSynced(pref)) {
         // Missing and default prefs get the null value.
         values[pref] = this._prefs.isSet(pref) ? this._prefs.get(pref, null) : null;
       }
     }
     return values;
   },
 
+  _updateLightWeightTheme (themeID) {
+    let themeObject = null;
+    if (themeID) {
+      themeObject = LightweightThemeManager.getUsedTheme(themeID);
+    }
+    LightweightThemeManager.currentTheme = themeObject;
+  },
+
   _setAllPrefs: function (values) {
     let selectedThemeIDPref = "lightweightThemes.selectedThemeID";
     let selectedThemeIDBefore = this._prefs.get(selectedThemeIDPref, null);
+    let selectedThemeIDAfter = selectedThemeIDBefore;
 
     // Update 'services.sync.prefs.sync.foo.pref' before 'foo.pref', otherwise
     // _isSynced returns false when 'foo.pref' doesn't exist (e.g., on a new device).
     let prefs = Object.keys(values).sort(a => -a.indexOf(PREF_SYNC_PREFS_PREFIX));
     for (let pref of prefs) {
       if (!this._isSynced(pref)) {
         continue;
       }
 
       let value = values[pref];
 
-      // Pref has gone missing. The best we can do is reset it.
-      if (value == null) {
-        this._prefs.reset(pref);
-        continue;
-      }
+      switch (pref) {
+        // Some special prefs we don't want to set directly.
+        case selectedThemeIDPref:
+          selectedThemeIDAfter = value;
+          break;
 
-      try {
-        this._prefs.set(pref, value);
-      } catch(ex) {
-        this._log.trace("Failed to set pref: " + pref + ": " + ex);
-      } 
+        // default is to just set the pref
+        default:
+          if (value == null) {
+            // Pref has gone missing. The best we can do is reset it.
+            this._prefs.reset(pref);
+          } else {
+            try {
+              this._prefs.set(pref, value);
+            } catch(ex) {
+              this._log.trace("Failed to set pref: " + pref + ": " + ex);
+            }
+          }
+      }
     }
 
     // Notify the lightweight theme manager if the selected theme has changed.
-    let selectedThemeIDAfter = this._prefs.get(selectedThemeIDPref, null);
     if (selectedThemeIDBefore != selectedThemeIDAfter) {
-      // The currentTheme getter will reflect the theme with the new
-      // selectedThemeID (if there is one).  Just reset it to itself
-      let currentTheme = LightweightThemeManager.currentTheme;
-      LightweightThemeManager.currentTheme = null;
-      LightweightThemeManager.currentTheme = currentTheme;
+      this._updateLightWeightTheme(selectedThemeIDAfter);
     }
   },
 
   getAllIDs: function () {
     /* We store all prefs in just one WBO, with just one GUID */
     let allprefs = {};
     allprefs[PREFS_GUID] = true;
     return allprefs;
--- a/services/sync/tests/unit/test_bookmark_engine.js
+++ b/services/sync/tests/unit/test_bookmark_engine.js
@@ -12,16 +12,113 @@ Cu.import("resource://services-sync/serv
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 Cu.import("resource://gre/modules/Promise.jsm");
 
 initTestLogging("Trace");
 
 Service.engineManager.register(BookmarksEngine);
 
+add_task(function* test_change_during_sync() {
+  _("Ensure that we track changes made during a sync.");
+
+  let engine = new BookmarksEngine(Service);
+  let store  = engine._store;
+  let server = serverForFoo(engine);
+  new SyncTestingInfrastructure(server.server);
+
+  let collection = server.user("foo").collection("bookmarks");
+
+  Svc.Obs.notify("weave:engine:start-tracking");
+
+  try {
+    let folder1_id = PlacesUtils.bookmarks.createFolder(
+      PlacesUtils.bookmarks.toolbarFolder, "Folder 1", 0);
+    let folder1_guid = store.GUIDForId(folder1_id);
+    _(`Folder GUID: ${folder1_guid}`);
+
+    let bmk1_id = PlacesUtils.bookmarks.insertBookmark(
+      folder1_id, Utils.makeURI("http://getthunderbird.com/"),
+      PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Thunderbird!");
+    let bmk1_guid = store.GUIDForId(bmk1_id);
+    _(`Thunderbird GUID: ${bmk1_guid}`);
+
+    // Sync is synchronous, so, to simulate a bookmark change made during a
+    // sync, we create a server record that adds a bookmark as a side effect.
+    let bmk2_guid = "get-firefox1";
+    let bmk3_id = -1;
+    {
+      let localRecord = new Bookmark("bookmarks", bmk2_guid);
+      localRecord.bmkUri        = "http://getfirefox.com/";
+      localRecord.description   = "Firefox is awesome.";
+      localRecord.title         = "Get Firefox!";
+      localRecord.tags          = ["firefox", "awesome", "browser"];
+      localRecord.keyword       = "awesome";
+      localRecord.loadInSidebar = false;
+      localRecord.parentName    = "Folder 1";
+      localRecord.parentid      = folder1_guid;
+
+      let remoteRecord = collection.insert(bmk2_guid, encryptPayload(localRecord.cleartext));
+      remoteRecord.get = function get() {
+        _("Inserting bookmark into local store");
+        bmk3_id = PlacesUtils.bookmarks.insertBookmark(
+          folder1_id, Utils.makeURI("https://mozilla.org/"),
+          PlacesUtils.bookmarks.DEFAULT_INDEX, "Mozilla");
+
+        return ServerWBO.prototype.get.apply(this, arguments);
+      };
+    }
+
+    {
+      let tree = yield PlacesUtils.promiseBookmarksTree(folder1_guid);
+      let childGuids = tree.children.map(child => child.guid);
+      deepEqual(childGuids, [bmk1_guid], "Folder should have 1 child before first sync");
+    }
+
+    _("Perform first sync");
+    yield sync_engine_and_validate_telem(engine, false);
+
+    let bmk2_id = store.idForGUID(bmk2_guid);
+    let bmk3_guid = store.GUIDForId(bmk3_id);
+    _(`Mozilla GUID: ${bmk3_guid}`);
+    {
+      equal(store.GUIDForId(bmk2_id), bmk2_guid,
+        "Remote bookmark should be applied during first sync");
+      ok(bmk3_id > -1,
+        "Bookmark created during first sync should exist locally");
+      ok(!collection.wbo(bmk3_guid),
+        "Bookmark created during first sync shouldn't be uploaded yet");
+
+      let tree = yield PlacesUtils.promiseBookmarksTree(folder1_guid);
+      let childGuids = tree.children.map(child => child.guid);
+      deepEqual(childGuids, [bmk1_guid, bmk3_guid, bmk2_guid],
+        "Folder should have 3 children after first sync");
+    }
+
+    _("Perform second sync");
+    yield sync_engine_and_validate_telem(engine, false);
+
+    {
+      ok(collection.wbo(bmk3_guid),
+        "Bookmark created during first sync should be uploaded during second sync");
+
+      let tree = yield PlacesUtils.promiseBookmarksTree(folder1_guid);
+      let childGuids = tree.children.map(child => child.guid);
+      deepEqual(childGuids, [bmk1_guid, bmk3_guid, bmk2_guid],
+        "Folder should have same children after second sync");
+    }
+  } finally {
+    store.wipe();
+    Svc.Prefs.resetBranch("");
+    Service.recordManager.clearCache();
+    yield new Promise(resolve => server.stop(resolve));
+    Svc.Obs.notify("weave:engine:stop-tracking");
+  }
+});
+
 add_task(function* bad_record_allIDs() {
   let server = new SyncServer();
   server.start();
   let syncTesting = new SyncTestingInfrastructure(server.server);
 
   _("Ensure that bad Places queries don't cause an error in getAllIDs.");
   let engine = new BookmarksEngine(Service);
   let store = engine._store;
--- a/services/sync/tests/unit/test_prefs_store.js
+++ b/services/sync/tests/unit/test_prefs_store.js
@@ -126,12 +126,43 @@ function run_test() {
     _("Only the current app's preferences are applied.");
     record = new PrefRec("prefs", "some-fake-app");
     record.value = {
       "testing.int": 98
     };
     store.update(record);
     do_check_eq(prefs.get("testing.int"), 42);
 
+    _("The light-weight theme preference is handled correctly.");
+    let lastThemeID = undefined;
+    let orig_updateLightWeightTheme = store._updateLightWeightTheme;
+    store._updateLightWeightTheme = function(themeID) {
+      lastThemeID = themeID;
+    }
+    try {
+      record = new PrefRec("prefs", PREFS_GUID);
+      record.value = {
+        "testing.int": 42,
+      };
+      store.update(record);
+      do_check_true(lastThemeID === undefined,
+                    "should not have tried to change the theme with an unrelated pref.");
+      Services.prefs.setCharPref("lightweightThemes.selectedThemeID", "foo");
+      record.value = {
+        "lightweightThemes.selectedThemeID": "foo",
+      };
+      store.update(record);
+      do_check_true(lastThemeID === undefined,
+                    "should not have tried to change the theme when the incoming pref matches current value.");
+
+      record.value = {
+        "lightweightThemes.selectedThemeID": "bar",
+      };
+      store.update(record);
+      do_check_eq(lastThemeID, "bar",
+                  "should have tried to change the theme when the incoming pref was different.");
+    } finally {
+      store._updateLightWeightTheme = orig_updateLightWeightTheme;
+    }
   } finally {
     prefs.resetBranch("");
   }
 }
--- a/testing/cppunittest.ini
+++ b/testing/cppunittest.ini
@@ -28,16 +28,17 @@ skip-if = os == 'b2g' || (os == 'android
 [TestCountZeroes]
 [TestDeadlockDetector]
 skip-if = os == 'b2g' || (os == 'android' && debug) # Bug 1054249
 [TestDeadlockDetectorScalability]
 [TestDllInterceptor]
 skip-if = os != 'win'
 [TestEndian]
 [TestEnumSet]
+[TestEnumTypeTraits]
 [TestFastBernoulliTrial]
 [TestFile]
 [TestFloatingPoint]
 [TestFunction]
 [TestGetURL]
 [TestHashtables]
 [TestID]
 [TestInitializerList]
deleted file mode 100644
--- a/toolkit/components/places/UnifiedComplete.manifest
+++ /dev/null
@@ -1,2 +0,0 @@
-component {f964a319-397a-4d21-8be6-5cdd1ee3e3ae} UnifiedComplete.js
-contract @mozilla.org/autocomplete/search;1?name=unifiedcomplete {f964a319-397a-4d21-8be6-5cdd1ee3e3ae}
--- a/toolkit/components/places/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/nsNavHistoryResult.cpp
@@ -3960,22 +3960,22 @@ nsNavHistoryFolderResultNode::OnItemMove
       NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
       nsresult rv = bookmarks->GetBookmarkURI(aItemId, getter_AddRefs(itemURI));
       NS_ENSURE_SUCCESS(rv, rv);
       rv = bookmarks->GetItemTitle(aItemId, itemTitle);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     if (aOldParent == mTargetFolderItemId) {
       OnItemRemoved(aItemId, aOldParent, aOldIndex, aItemType, itemURI,
-                    aGUID, aOldParentGUID, nsINavBookmarksService::SOURCE_DEFAULT);
+                    aGUID, aOldParentGUID, aSource);
     }
     if (aNewParent == mTargetFolderItemId) {
       OnItemAdded(aItemId, aNewParent, aNewIndex, aItemType, itemURI, itemTitle,
                   RoundedPRNow(), // This is a dummy dateAdded, not the real value.
-                  aGUID, aNewParentGUID, nsINavBookmarksService::SOURCE_DEFAULT);
+                  aGUID, aNewParentGUID, aSource);
     }
   }
   return NS_OK;
 }
 
 
 /**
  * Separator nodes do not hold any data.
--- a/toolkit/components/telemetry/nsITelemetry.idl
+++ b/toolkit/components/telemetry/nsITelemetry.idl
@@ -57,17 +57,16 @@ interface nsITelemetry : nsISupports
    * where data is consists of the following properties:
    *   min - Minimal bucket size
    *   max - Maximum bucket size
    *   histogram_type - HISTOGRAM_EXPONENTIAL, HISTOGRAM_LINEAR, HISTOGRAM_BOOLEAN
    *                    or HISTOGRAM_COUNT
    *   counts - array representing contents of the buckets in the histogram
    *   ranges -  an array with calculated bucket sizes
    *   sum - sum of the bucket contents
-   *   static - true for histograms defined in TelemetryHistograms.h, false for ones defined with newHistogram
    */
   [implicit_jscontext]
   readonly attribute jsval histogramSnapshots;
 
   /**
    * Get a snapshot of the internally duplicated subsession histograms.
    * @param clear Whether to clear out the subsession histograms after snapshotting.
    * @return An object as histogramSnapshots, except this contains the internally duplicated histograms for subsession telemetry.
--- a/toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html
@@ -291,14 +291,15 @@ function runTest() {
 SimpleTest.waitForExplicitFinish();
 
 // 'network.predictor.enabled' is disabled because if other testcase load
 // evil.js, evil.css ...etc resources, it may cause we load them from cache
 // directly and bypass classifier check
 SpecialPowers.pushPrefEnv({"set": [
   ["browser.safebrowsing.malware.enabled", true],
   ["network.predictor.enabled", false],
+  ["urlclassifier.gethash.timeout_ms", 30000],
 ]}, runTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/url-classifier/tests/mochitest/test_gethash.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_gethash.html
@@ -143,14 +143,15 @@ function runTest() {
 SimpleTest.waitForExplicitFinish();
 
 // 'network.predictor.enabled' is disabled because if other testcase load
 // evil.js, evil.css ...etc resources, it may cause we load them from cache
 // directly and bypass classifier check
 SpecialPowers.pushPrefEnv({"set": [
   ["browser.safebrowsing.malware.enabled", true],
   ["network.predictor.enabled", false],
+  ["urlclassifier.gethash.timeout_ms", 30000],
 ]}, runTest);
 
 </script>
 </pre>
 </body>
 </html>