Merge autoland to mozilla-central. a=merge
authorshindli <shindli@mozilla.com>
Tue, 19 Jun 2018 00:39:01 +0300
changeset 422853 854d98803f6925fc8cb96f4ded4d7ad7de3e5922
parent 422832 681eb7e9d29634d57dc34b08119483ee1745ef11 (current diff)
parent 422852 c2ced94f52aade1532494cfdd31d6fdacd3f1edf (diff)
child 422868 1e2c9151a09e43613a79daa8d4a94dc3e314020c
push id34155
push usershindli@mozilla.com
push dateMon, 18 Jun 2018 21:39:31 +0000
treeherdermozilla-central@854d98803f69 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
--- a/browser/base/content/test/pageinfo/browser_pageinfo_image_info.js
+++ b/browser/base/content/test/pageinfo/browser_pageinfo_image_info.js
@@ -17,19 +17,18 @@ const URI =
   "<img src='about:logo?b' height=200 width=250 alt=1>" +
   "<img src='about:logo?b' height=100 width=150 alt=2 id='test-image'>";
 
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
 
-  let uriToWaitFor = URI.replace(/ /g, "%20");
   BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false,
-                                 uriToWaitFor).then(function() {
+                                 URI).then(function() {
     var doc = gBrowser.contentDocumentAsCPOW;
     var testImg = doc.getElementById("test-image");
     var pageInfo = BrowserPageInfo(gBrowser.selectedBrowser.currentURI.spec,
                                    "mediaTab", getImageInfo(testImg));
 
     pageInfo.addEventListener("load", function() {
       pageInfo.onFinished.push(function() {
         var pageInfoImg = pageInfo.document.getElementById("thepreviewimage");
--- a/browser/components/sessionstore/test/browser_formdata.js
+++ b/browser/components/sessionstore/test/browser_formdata.js
@@ -57,17 +57,17 @@ add_task(async function test_formdata() 
 });
 
 /**
  * This test ensures that a malicious website can't trick us into restoring
  * form data into a wrong website and that we always check the stored URL
  * before doing so.
  */
 add_task(async function test_url_check() {
-  const URL = "data:text/html;charset=utf-8,<input%20id=input>";
+  const URL = "data:text/html;charset=utf-8,<input id=input>";
   const VALUE = "value-" + Math.random();
 
   // Create a tab with an iframe containing an input field.
   let tab = BrowserTestUtils.addTab(gBrowser, URL);
   let browser = tab.linkedBrowser;
   await promiseBrowserLoaded(browser);
 
   // Restore a tab state with a given form data url.
@@ -99,17 +99,17 @@ add_task(async function test_url_check()
 add_task(async function test_nested() {
   const URL = "data:text/html;charset=utf-8," +
               "<iframe src='data:text/html;charset=utf-8," +
               "<input autofocus=true>'/>";
 
   const FORM_DATA = {
     children: [{
       xpath: {"/xhtml:html/xhtml:body/xhtml:input": "m"},
-      url: "data:text/html;charset=utf-8,<input%20autofocus=true>"
+      url: "data:text/html;charset=utf-8,<input autofocus=true>"
     }]
   };
 
   // Create a tab with an iframe containing an input field.
   let tab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, URL);
   let browser = tab.linkedBrowser;
   await promiseBrowserLoaded(browser);
 
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_context_menu_copy_entire_message.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_context_menu_copy_entire_message.js
@@ -12,17 +12,17 @@ const LOG_FORMAT_WITH_TIMESTAMP = /^[\d:
 const LOG_FORMAT_WITHOUT_TIMESTAMP = /^.+ (\d+ )?.+:\d+$/;
 // RegExp that validates copied text for stacktrace lines.
 const TRACE_FORMAT = /^\t.+ .+:\d+:\d+$/;
 
 const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
 
 const TEST_URI = `data:text/html;charset=utf-8,<script>
   window.logStuff = function () {
-    console.log("simple text message");
+    console.log("simple " +  "text message");
     function wrapper() {
       console.trace();
     }
     wrapper();
   };
 </script>`;
 
 // Test the Copy menu item of the webconsole copies the expected clipboard text for
@@ -44,29 +44,29 @@ add_task(async function() {
 
   info("Test copy menu item for the simple log");
   let message = await waitFor(() => findMessage(hud, "simple text message"));
   let clipboardText = await copyMessageContent(hud, message);
   ok(true, "Clipboard text was found and saved");
 
   info("Check copied text for simple log message");
   let lines = clipboardText.split("\n");
-  ok(lines.length, 2, "There are 2 lines in the copied text");
+  is(lines.length, 2, "There are 2 lines in the copied text");
   is(lines[1], "", "The last line is an empty new line");
   ok(LOG_FORMAT_WITH_TIMESTAMP.test(lines[0]),
     "Log line has the right format:\n" + lines[0]);
 
   info("Test copy menu item for the stack trace message");
   message = await waitFor(() => findMessage(hud, "console.trace"));
   clipboardText = await copyMessageContent(hud, message);
   ok(true, "Clipboard text was found and saved");
 
   info("Check copied text for stack trace message");
   lines = clipboardText.split("\n");
-  ok(lines.length, 4, "There are 4 lines in the copied text");
+  is(lines.length, 4, "There are 4 lines in the copied text");
   is(lines[3], "", "The last line is an empty new line");
   ok(LOG_FORMAT_WITH_TIMESTAMP.test(lines[0]),
     "Log line has the right format:\n" + lines[0]);
   ok(TRACE_FORMAT.test(lines[1]), "Stacktrace line has the right format:\n" + lines[1]);
   ok(TRACE_FORMAT.test(lines[2]), "Stacktrace line has the right format:\n" + lines[2]);
 
   info("Test copy menu item without timestamp");
 
@@ -76,29 +76,29 @@ add_task(async function() {
 
   info("Test copy menu item for the simple log");
   message = await waitFor(() => findMessage(hud, "simple text message"));
   clipboardText = await copyMessageContent(hud, message);
   ok(true, "Clipboard text was found and saved");
 
   info("Check copied text for simple log message");
   lines = clipboardText.split("\n");
-  ok(lines.length, 2, "There are 2 lines in the copied text");
+  is(lines.length, 2, "There are 2 lines in the copied text");
   is(lines[1], "", "The last line is an empty new line");
   ok(LOG_FORMAT_WITHOUT_TIMESTAMP.test(lines[0]),
     "Log line has the right format:\n" + lines[0]);
 
   info("Test copy menu item for the stack trace message");
   message = await waitFor(() => findMessage(hud, "console.trace"));
   clipboardText = await copyMessageContent(hud, message);
   ok(true, "Clipboard text was found and saved");
 
   info("Check copied text for stack trace message");
   lines = clipboardText.split("\n");
-  ok(lines.length, 4, "There are 4 lines in the copied text");
+  is(lines.length, 4, "There are 4 lines in the copied text");
   is(lines[3], "", "The last line is an empty new line");
   ok(LOG_FORMAT_WITHOUT_TIMESTAMP.test(lines[0]),
     "Log line has the right format:\n" + lines[0]);
   ok(TRACE_FORMAT.test(lines[1]), "Stacktrace line has the right format:\n" + lines[1]);
   ok(TRACE_FORMAT.test(lines[2]), "Stacktrace line has the right format:\n" + lines[2]);
 
   observer.destroy();
   Services.prefs.clearUserPref(PREF_MESSAGE_TIMESTAMP);
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -55,19 +55,17 @@ skip-if = os == 'linux' && debug # Bug 1
 [test_getUserMedia_audioConstraints.html]
 skip-if = os == 'mac' || os == 'win' || toolkit == 'android' # Bug 1404995, no loopback devices on some platforms
 [test_getUserMedia_basicAudio_loopback.html]
 skip-if = os == 'mac' || os == 'win' || toolkit == 'android' # Bug 1404995, no loopback devices on some platforms
 [test_getUserMedia_basicAudio.html]
 [test_getUserMedia_basicVideo.html]
 [test_getUserMedia_basicVideo_playAfterLoadedmetadata.html]
 [test_getUserMedia_basicScreenshare.html]
-#skip-if = toolkit == 'android' # no screenshare on android
-# When re-enabling this test, uncomment the line above.
-disabled=bug 1405083
+skip-if = toolkit == 'android' # no screenshare on android
 [test_getUserMedia_basicTabshare.html]
 skip-if = toolkit == 'android' # no windowshare on android
 [test_getUserMedia_basicWindowshare.html]
 skip-if = toolkit == 'android' # no windowshare on android
 [test_getUserMedia_basicVideoAudio.html]
 [test_getUserMedia_bug1223696.html]
 [test_getUserMedia_constraints.html]
 [test_getUserMedia_callbacks.html]
--- a/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp
+++ b/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp
@@ -388,17 +388,17 @@ SpeechDispatcherService::Setup()
   spd_set_notification_on(mSpeechdClient, SPD_CANCEL);
 
   if (list != NULL) {
     for (int i = 0; list[i]; i++) {
       nsAutoString uri;
 
       uri.AssignLiteral(URI_PREFIX);
       nsAutoCString name;
-      NS_EscapeURL(list[i]->name, -1, esc_OnlyNonASCII | esc_AlwaysCopy, name);
+      NS_EscapeURL(list[i]->name, -1, esc_OnlyNonASCII | esc_Spaces | esc_AlwaysCopy, name);
       uri.Append(NS_ConvertUTF8toUTF16(name));;
       uri.AppendLiteral("?");
 
       nsAutoCString lang(list[i]->language);
 
       if (strcmp(list[i]->variant, "none") != 0) {
         // In speech dispatcher, the variant will usually be the locale subtag
         // with another, non-standard suptag after it. We keep the first one
--- a/dom/url/tests/test_url.html
+++ b/dom/url/tests/test_url.html
@@ -474,17 +474,17 @@
     url.hash = "newhash";
     is(url.href, "scheme:path?query#newhash");
     url.search = "";
     is(url.href, "scheme:path#newhash");
 
     // we don't implement a spec-compliant parser yet.
     // make sure we are bug compatible with existing implementations.
     url = new URL("data:text/html,<a href=\"http://example.org/?q\">Link</a>");
-    is(url.href, "data:text/html,<a%20href=\"http://example.org/?q\">Link</a>");
+    is(url.href, "data:text/html,<a href=\"http://example.org/?q\">Link</a>");
   </script>
 
   <script>
     var u = new URL('http://www.example.org');
     ok(u.toJSON(), 'http://www.example.org', "URL.toJSON()");
     is(JSON.stringify(u), "\"http://www.example.org/\"", "JSON.stringify(u) works");
   </script>
 </body>
--- a/dom/xbl/nsXBLWindowKeyHandler.cpp
+++ b/dom/xbl/nsXBLWindowKeyHandler.cpp
@@ -38,34 +38,29 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
 class nsXBLSpecialDocInfo : public nsIObserver
 {
 public:
   RefPtr<nsXBLDocumentInfo> mHTMLBindings;
-  RefPtr<nsXBLDocumentInfo> mUserHTMLBindings;
 
   static const char sHTMLBindingStr[];
   static const char sUserHTMLBindingStr[];
 
   bool mInitialized;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   void LoadDocInfo();
-  void GetAllHandlers(const char* aType,
-                      nsXBLPrototypeHandler** handler,
-                      nsXBLPrototypeHandler** userHandler);
-  void GetHandlers(nsXBLDocumentInfo* aInfo,
-                   const nsACString& aRef,
-                   nsXBLPrototypeHandler** aResult);
+  void GetHandlers(const nsACString& aRef,
+                   nsXBLPrototypeHandler** handler);
 
   nsXBLSpecialDocInfo() : mInitialized(false) {}
 
 protected:
   virtual ~nsXBLSpecialDocInfo() {}
 
 };
 
@@ -78,17 +73,16 @@ NS_IMETHODIMP
 nsXBLSpecialDocInfo::Observe(nsISupports* aSubject,
                              const char* aTopic,
                              const char16_t* aData)
 {
   MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"), "wrong topic");
 
   // On shutdown, clear our fields to avoid an extra cycle collection.
   mHTMLBindings = nullptr;
-  mUserHTMLBindings = nullptr;
   mInitialized = false;
   nsContentUtils::UnregisterShutdownObserver(this);
 
   return NS_OK;
 }
 
 void nsXBLSpecialDocInfo::LoadDocInfo()
 {
@@ -114,41 +108,27 @@ void nsXBLSpecialDocInfo::LoadDocInfo()
                                       getter_AddRefs(mHTMLBindings));
 }
 
 //
 // GetHandlers
 //
 //
 void
-nsXBLSpecialDocInfo::GetHandlers(nsXBLDocumentInfo* aInfo,
-                                 const nsACString& aRef,
-                                 nsXBLPrototypeHandler** aResult)
+nsXBLSpecialDocInfo::GetHandlers(const nsACString& aRef,
+                                 nsXBLPrototypeHandler** aHandler)
 {
-  nsXBLPrototypeBinding* binding = aInfo->GetPrototypeBinding(aRef);
-
-  NS_ASSERTION(binding, "No binding found for the XBL window key handler.");
-  if (!binding)
-    return;
-
-  *aResult = binding->GetPrototypeHandlers();
-}
+  if (mHTMLBindings) {
+    nsXBLPrototypeBinding* binding = mHTMLBindings->GetPrototypeBinding(aRef);
 
-void
-nsXBLSpecialDocInfo::GetAllHandlers(const char* aType,
-                                    nsXBLPrototypeHandler** aHandler,
-                                    nsXBLPrototypeHandler** aUserHandler)
-{
-  if (mUserHTMLBindings) {
-    nsAutoCString type(aType);
-    type.AppendLiteral("User");
-    GetHandlers(mUserHTMLBindings, type, aUserHandler);
-  }
-  if (mHTMLBindings) {
-    GetHandlers(mHTMLBindings, nsDependentCString(aType), aHandler);
+    NS_ASSERTION(binding, "No binding found for the XBL window key handler.");
+    if (!binding)
+      return;
+
+    *aHandler = binding->GetPrototypeHandlers();
   }
 }
 
 // Init statics
 static StaticRefPtr<nsXBLSpecialDocInfo> sXBLSpecialDocInfo;
 uint32_t nsXBLWindowKeyHandler::sRefCnt = 0;
 
 /* static */ void
@@ -158,18 +138,17 @@ nsXBLWindowKeyHandler::EnsureSpecialDocI
     sXBLSpecialDocInfo = new nsXBLSpecialDocInfo();
   }
   sXBLSpecialDocInfo->LoadDocInfo();
 }
 
 nsXBLWindowKeyHandler::nsXBLWindowKeyHandler(Element* aElement,
                                              EventTarget* aTarget)
   : mTarget(aTarget),
-    mHandler(nullptr),
-    mUserHandler(nullptr)
+    mHandler(nullptr)
 {
   mWeakPtrForElement = do_GetWeakReference(aElement);
   ++sRefCnt;
 }
 
 nsXBLWindowKeyHandler::~nsXBLWindowKeyHandler()
 {
   // If mWeakPtrForElement is non-null, we created a prototype handler.
@@ -251,20 +230,20 @@ nsXBLWindowKeyHandler::EnsureHandlers()
 
     nsCOMPtr<nsIContent> content(do_QueryInterface(el));
     BuildHandlerChain(content, &mHandler);
   } else { // We are an XBL file of handlers.
     EnsureSpecialDocInfo();
 
     // Now determine which handlers we should be using.
     if (IsHTMLEditableFieldFocused()) {
-      sXBLSpecialDocInfo->GetAllHandlers("editor", &mHandler, &mUserHandler);
+      sXBLSpecialDocInfo->GetHandlers(NS_LITERAL_CSTRING("editor"), &mHandler);
     }
     else {
-      sXBLSpecialDocInfo->GetAllHandlers("browser", &mHandler, &mUserHandler);
+      sXBLSpecialDocInfo->GetHandlers(NS_LITERAL_CSTRING("browser"), &mHandler);
     }
   }
 
   return NS_OK;
 }
 
 nsresult
 nsXBLWindowKeyHandler::WalkHandlers(KeyboardEvent* aKeyEvent, nsAtom* aEventType)
@@ -278,31 +257,23 @@ nsXBLWindowKeyHandler::WalkHandlers(Keyb
     return NS_OK;
   }
 
   nsresult rv = EnsureHandlers();
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool isDisabled;
   nsCOMPtr<Element> el = GetElement(&isDisabled);
-  if (!el) {
-    if (mUserHandler) {
-      WalkHandlersInternal(aKeyEvent, aEventType, mUserHandler, true);
-      if (aKeyEvent->DefaultPrevented()) {
-        return NS_OK; // Handled by the user bindings. Our work here is done.
-      }
-    }
-  }
 
   // skip keysets that are disabled
   if (el && isDisabled) {
     return NS_OK;
   }
 
-  WalkHandlersInternal(aKeyEvent, aEventType, mHandler, true);
+  WalkHandlersInternal(aKeyEvent, aEventType, true);
 
   return NS_OK;
 }
 
 void
 nsXBLWindowKeyHandler::InstallKeyboardEventListenersTo(
                          EventListenerManager* aEventListenerManager)
 {
@@ -425,18 +396,17 @@ nsXBLWindowKeyHandler::RemoveKeyboardEve
 
 /* static */ KeyboardMap
 nsXBLWindowKeyHandler::CollectKeyboardShortcuts()
 {
   // Load the XBL handlers
   EnsureSpecialDocInfo();
 
   nsXBLPrototypeHandler* handlers = nullptr;
-  nsXBLPrototypeHandler* userHandlers = nullptr;
-  sXBLSpecialDocInfo->GetAllHandlers("browser", &handlers, &userHandlers);
+  sXBLSpecialDocInfo->GetHandlers(NS_LITERAL_CSTRING("browser"), &handlers);
 
   // Convert the handlers into keyboard shortcuts, using an AutoTArray with
   // the maximum amount of shortcuts used on any platform to minimize allocations
   AutoTArray<KeyboardShortcut, 48> shortcuts;
 
   // Append keyboard shortcuts for hardcoded actions like tab
   KeyboardShortcut::AppendHardcodedShortcuts(shortcuts);
 
@@ -444,25 +414,16 @@ nsXBLWindowKeyHandler::CollectKeyboardSh
        handler;
        handler = handler->GetNextHandler()) {
     KeyboardShortcut shortcut;
     if (handler->TryConvertToKeyboardShortcut(&shortcut)) {
       shortcuts.AppendElement(shortcut);
     }
   }
 
-  for (nsXBLPrototypeHandler* handler = userHandlers;
-       handler;
-       handler = handler->GetNextHandler()) {
-    KeyboardShortcut shortcut;
-    if (handler->TryConvertToKeyboardShortcut(&shortcut)) {
-      shortcuts.AppendElement(shortcut);
-    }
-  }
-
   return KeyboardMap(std::move(shortcuts));
 }
 
 nsAtom*
 nsXBLWindowKeyHandler::ConvertEventToDOMEventType(
                          const WidgetKeyboardEvent& aWidgetKeyboardEvent) const
 {
   if (aWidgetKeyboardEvent.IsKeyDownOrKeyDownOnPlugin()) {
@@ -638,68 +599,66 @@ nsXBLWindowKeyHandler::IsHTMLEditableFie
 // Given a particular DOM event and a pointer to the first handler in the list,
 // scan through the list to find something to handle the event. If aExecute = true,
 // the handler will be executed; otherwise just return an answer telling if a handler
 // for that event was found.
 //
 bool
 nsXBLWindowKeyHandler::WalkHandlersInternal(KeyboardEvent* aKeyEvent,
                                             nsAtom* aEventType,
-                                            nsXBLPrototypeHandler* aHandler,
                                             bool aExecute,
                                             bool* aOutReservedForChrome)
 {
   WidgetKeyboardEvent* nativeKeyboardEvent =
     aKeyEvent->WidgetEventPtr()->AsKeyboardEvent();
   MOZ_ASSERT(nativeKeyboardEvent);
 
   AutoShortcutKeyCandidateArray shortcutKeys;
   nativeKeyboardEvent->GetShortcutKeyCandidates(shortcutKeys);
 
   if (shortcutKeys.IsEmpty()) {
-    return WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
+    return WalkHandlersAndExecute(aKeyEvent, aEventType,
                                   0, IgnoreModifierState(),
                                   aExecute, aOutReservedForChrome);
   }
 
   for (uint32_t i = 0; i < shortcutKeys.Length(); ++i) {
     ShortcutKeyCandidate& key = shortcutKeys[i];
     IgnoreModifierState ignoreModifierState;
     ignoreModifierState.mShift = key.mIgnoreShift;
-    if (WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
+    if (WalkHandlersAndExecute(aKeyEvent, aEventType,
                                key.mCharCode, ignoreModifierState,
                                aExecute, aOutReservedForChrome)) {
       return true;
     }
   }
   return false;
 }
 
 bool
 nsXBLWindowKeyHandler::WalkHandlersAndExecute(
                          KeyboardEvent* aKeyEvent,
                          nsAtom* aEventType,
-                         nsXBLPrototypeHandler* aFirstHandler,
                          uint32_t aCharCode,
                          const IgnoreModifierState& aIgnoreModifierState,
                          bool aExecute,
                          bool* aOutReservedForChrome)
 {
   if (aOutReservedForChrome) {
     *aOutReservedForChrome = false;
   }
 
   WidgetKeyboardEvent* widgetKeyboardEvent =
     aKeyEvent->WidgetEventPtr()->AsKeyboardEvent();
   if (NS_WARN_IF(!widgetKeyboardEvent)) {
     return false;
   }
 
   // Try all of the handlers until we find one that matches the event.
-  for (nsXBLPrototypeHandler* handler = aFirstHandler;
+  for (nsXBLPrototypeHandler* handler = mHandler;
        handler;
        handler = handler->GetNextHandler()) {
     bool stopped = aKeyEvent->IsDispatchStopped();
     if (stopped) {
       // The event is finished, don't execute any more handlers
       return false;
     }
 
@@ -810,17 +769,17 @@ nsXBLWindowKeyHandler::WalkHandlersAndEx
 #ifdef XP_WIN
   // Windows native applications ignore Windows-Logo key state when checking
   // shortcut keys even if the key is pressed.  Therefore, if there is no
   // shortcut key which exactly matches current modifier state, we should
   // retry to look for a shortcut key without the Windows-Logo key press.
   if (!aIgnoreModifierState.mOS && widgetKeyboardEvent->IsOS()) {
     IgnoreModifierState ignoreModifierState(aIgnoreModifierState);
     ignoreModifierState.mOS = true;
-    return WalkHandlersAndExecute(aKeyEvent, aEventType, aFirstHandler,
+    return WalkHandlersAndExecute(aKeyEvent, aEventType,
                                   aCharCode, ignoreModifierState, aExecute);
   }
 #endif
 
   return false;
 }
 
 bool
@@ -858,17 +817,17 @@ nsXBLWindowKeyHandler::HasHandlerForEven
   bool isDisabled;
   nsCOMPtr<Element> el = GetElement(&isDisabled);
   if (el && isDisabled) {
     return false;
   }
 
   RefPtr<nsAtom> eventTypeAtom =
     ConvertEventToDOMEventType(*widgetKeyboardEvent);
-  return WalkHandlersInternal(aEvent, eventTypeAtom, mHandler, false,
+  return WalkHandlersInternal(aEvent, eventTypeAtom, false,
                               aOutReservedForChrome);
 }
 
 already_AddRefed<Element>
 nsXBLWindowKeyHandler::GetElement(bool* aIsDisabled)
 {
   nsCOMPtr<Element> element = do_QueryReferent(mWeakPtrForElement);
   if (element && aIsDisabled) {
--- a/dom/xbl/nsXBLWindowKeyHandler.h
+++ b/dom/xbl/nsXBLWindowKeyHandler.h
@@ -50,24 +50,22 @@ public:
 protected:
   virtual ~nsXBLWindowKeyHandler();
 
   nsresult WalkHandlers(KeyboardEvent* aKeyEvent, nsAtom* aEventType);
 
   // walk the handlers, looking for one to handle the event
   bool WalkHandlersInternal(KeyboardEvent* aKeyEvent,
                             nsAtom* aEventType,
-                            nsXBLPrototypeHandler* aHandler,
                             bool aExecute,
                             bool* aOutReservedForChrome = nullptr);
 
   // walk the handlers for aEvent, aCharCode and aIgnoreModifierState. Execute
   // it if aExecute = true.
   bool WalkHandlersAndExecute(KeyboardEvent* aKeyEvent, nsAtom* aEventType,
-                              nsXBLPrototypeHandler* aHandler,
                               uint32_t aCharCode,
                               const IgnoreModifierState& aIgnoreModifierState,
                               bool aExecute,
                               bool* aOutReservedForChrome = nullptr);
 
   // HandleEvent function for the capturing phase in the default event group.
   void HandleEventOnCaptureInDefaultEventGroup(KeyboardEvent* aEvent);
   // HandleEvent function for the capturing phase in the system event group.
@@ -126,17 +124,16 @@ protected:
 
   // Using weak pointer to the DOM Element.
   nsWeakPtr              mWeakPtrForElement;
   mozilla::dom::EventTarget* mTarget; // weak ref
 
   // these are not owning references; the prototype handlers are owned
   // by the prototype bindings which are owned by the docinfo.
   nsXBLPrototypeHandler* mHandler;     // platform bindings
-  nsXBLPrototypeHandler* mUserHandler; // user-specific bindings
 
   // holds reference count to document info about bindings
   static uint32_t sRefCnt;
 };
 
 already_AddRefed<nsXBLWindowKeyHandler>
 NS_NewXBLWindowKeyHandler(mozilla::dom::Element* aElement,
                           mozilla::dom::EventTarget* aTarget);
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -433,28 +433,26 @@ public:
 #endif
     , mInvalidateAllLayers(false)
   {
     MOZ_COUNT_CTOR(LayerManagerData);
   }
   ~LayerManagerData() {
     MOZ_COUNT_DTOR(LayerManagerData);
 
-    for (auto iter = mDisplayItems.Iter(); !iter.Done(); iter.Next()) {
-      iter.Get()->GetKey()->Disconnect();
+    for (auto& item : mDisplayItems) {
+      item->Disconnect();
     }
   }
 
 #ifdef DEBUG_DISPLAY_ITEM_DATA
   void Dump(const char *aPrefix = "") {
     printf_stderr("%sLayerManagerData %p\n", aPrefix, this);
 
-    for (auto iter = mDisplayItems.Iter(); !iter.Done(); iter.Next()) {
-      FrameLayerBuilder::DisplayItemData* data = iter.Get()->GetKey();
-
+    for (auto& data : mDisplayItems) {
       nsAutoCString prefix;
       prefix += aPrefix;
       prefix += "  ";
 
       const char* layerState;
       switch (data->mLayerState) {
       case LAYER_NONE:
         layerState = "LAYER_NONE"; break;
@@ -502,17 +500,17 @@ public:
 
   /**
    * Tracks which frames have layers associated with them.
    */
   LayerManager *mLayerManager;
 #ifdef DEBUG_DISPLAY_ITEM_DATA
   LayerManagerData *mParent;
 #endif
-  nsTHashtable<nsRefPtrHashKey<DisplayItemData> > mDisplayItems;
+  std::vector<RefPtr<DisplayItemData>> mDisplayItems;
   bool mInvalidateAllLayers;
 };
 
 /* static */ void
 FrameLayerBuilder::DestroyDisplayItemDataFor(nsIFrame* aFrame)
 {
   RemoveFrameFromLayerManager(aFrame, aFrame->DisplayItemData());
   aFrame->DisplayItemData().Clear();
@@ -2144,17 +2142,22 @@ FrameLayerBuilder::RemoveFrameFromLayerM
         nsIntRegion rgn = old.ScaleToOutsidePixels(paintedData->mXScale, paintedData->mYScale, paintedData->mAppUnitsPerDevPixel);
         rgn.MoveBy(-GetTranslationForPaintedLayer(t));
         paintedData->mRegionToInvalidate.Or(paintedData->mRegionToInvalidate, rgn);
         paintedData->mRegionToInvalidate.SimplifyOutward(8);
       }
     }
 
     data->Disconnect();
-    data->mParent->mDisplayItems.RemoveEntry(data);
+    auto it = std::find(data->mParent->mDisplayItems.begin(),
+                        data->mParent->mDisplayItems.end(),
+                        data);
+    MOZ_ASSERT(it != data->mParent->mDisplayItems.end());
+    std::iter_swap(it, data->mParent->mDisplayItems.end() - 1);
+    data->mParent->mDisplayItems.pop_back();
   }
 
   arrayCopy.Clear();
   sDestroyedFrame = nullptr;
 }
 
 void
 FrameLayerBuilder::DidBeginRetainedLayerTransaction(LayerManager* aManager)
@@ -2184,38 +2187,52 @@ FrameLayerBuilder::WillEndTransaction()
   }
 
   // We need to save the data we'll need to support retaining.
   LayerManagerData* data = static_cast<LayerManagerData*>
     (mRetainingManager->GetUserData(&gLayerManagerUserData));
   NS_ASSERTION(data, "Must have data!");
 
   // Update all the frames that used to have layers.
-  for (auto iter = data->mDisplayItems.Iter(); !iter.Done(); iter.Next()) {
-    DisplayItemData* data = iter.Get()->GetKey();
-    if (!data->mUsed) {
+  auto iter = data->mDisplayItems.begin();
+  while (iter != data->mDisplayItems.end()) {
+    DisplayItemData* did = iter->get();
+    if (!did->mUsed) {
       // This item was visible, but isn't anymore.
-      PaintedLayer* t = data->mLayer->AsPaintedLayer();
-      if (t && data->mGeometry) {
+      PaintedLayer* t = did->mLayer->AsPaintedLayer();
+      if (t && did->mGeometry) {
 #ifdef MOZ_DUMP_PAINTING
         if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
-          printf_stderr("Invalidating unused display item (%i) belonging to frame %p from layer %p\n", data->mDisplayItemKey, data->mFrameList[0], t);
+          printf_stderr("Invalidating unused display item (%i) belonging to frame %p from layer %p\n", did->mDisplayItemKey, did->mFrameList[0], t);
         }
 #endif
         InvalidatePostTransformRegion(t,
-                                      data->mGeometry->ComputeInvalidationRegion(),
-                                      data->mClip,
+                                      did->mGeometry->ComputeInvalidationRegion(),
+                                      did->mClip,
                                       GetLastPaintOffset(t));
       }
 
-      data->ClearAnimationCompositorState();
-      data->Disconnect();
-      iter.Remove();
+      did->ClearAnimationCompositorState();
+      did->Disconnect();
+
+      // Remove this item. Swapping it with the last element first is
+      // quicker than erasing from the middle.
+      if (iter != data->mDisplayItems.end() - 1) {
+        std::iter_swap(iter, data->mDisplayItems.end() - 1);
+        data->mDisplayItems.pop_back();
+      } else {
+        data->mDisplayItems.pop_back();
+        break;
+      }
+
+      // Don't increment iter because we still need to process the item which was moved.
+
     } else {
-      ComputeGeometryChangeForItem(data);
+      ComputeGeometryChangeForItem(did);
+      iter++;
     }
   }
 
   data->mInvalidateAllLayers = false;
 }
 
 /* static */ DisplayItemData*
 FrameLayerBuilder::GetDisplayItemDataForManager(nsDisplayItem* aItem,
@@ -5116,17 +5133,17 @@ FrameLayerBuilder::StoreDataForFrame(nsD
     new (aItem->Frame()->PresContext()) DisplayItemData(lmd, aItem->GetPerFrameKey(), aLayer);
 
   if (!data->HasMergedFrames()) {
     aItem->SetDisplayItemData(data);
   }
 
   data->BeginUpdate(aLayer, aState, true, aItem);
 
-  lmd->mDisplayItems.PutEntry(data);
+  lmd->mDisplayItems.push_back(data);
   return data;
 }
 
 void
 FrameLayerBuilder::StoreDataForFrame(nsIFrame* aFrame,
                                      uint32_t aDisplayItemKey,
                                      Layer* aLayer,
                                      LayerState aState)
@@ -5140,17 +5157,17 @@ FrameLayerBuilder::StoreDataForFrame(nsI
   LayerManagerData* lmd = static_cast<LayerManagerData*>
     (mRetainingManager->GetUserData(&gLayerManagerUserData));
 
   RefPtr<DisplayItemData> data =
     new (aFrame->PresContext()) DisplayItemData(lmd, aDisplayItemKey, aLayer, aFrame);
 
   data->BeginUpdate(aLayer, aState, true);
 
-  lmd->mDisplayItems.PutEntry(data);
+  lmd->mDisplayItems.push_back(data);
 }
 
 AssignedDisplayItem::AssignedDisplayItem(nsDisplayItem* aItem,
                                          LayerState aLayerState,
                                          DisplayItemData* aData,
                                          const nsRect& aContentRect,
                                          DisplayItemEntryType aType,
                                          const bool aHasOpacity)
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -1181,49 +1181,49 @@ var gCSSProperties = {
     other_values: [ "none none 0s 0s cubic-bezier(0.25, 0.1, 0.25, 1.0) normal running 1.0", "bounce 1s linear 2s", "bounce 1s 2s linear", "bounce linear 1s 2s", "linear bounce 1s 2s", "linear 1s bounce 2s", "linear 1s 2s bounce", "1s bounce linear 2s", "1s bounce 2s linear", "1s 2s bounce linear", "1s linear bounce 2s", "1s linear 2s bounce", "1s 2s linear bounce", "bounce linear 1s", "bounce 1s linear", "linear bounce 1s", "linear 1s bounce", "1s bounce linear", "1s linear bounce", "1s 2s bounce", "1s bounce 2s", "bounce 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "bounce 1s", "1s bounce", "linear 1s", "1s linear", "1s 2s", "2s 1s", "bounce", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s bounce, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32bounce linear 2s", "1s -bounce linear 2s", "1s -\\32bounce linear 2s", "1s \\32 0bounce linear 2s", "1s -\\32 0bounce linear 2s", "1s \\2bounce linear 2s", "1s -\\2bounce linear 2s", "2s, 1s bounce", "1s bounce, 2s", "2s all, 1s bounce", "1s bounce, 2s all", "1s bounce, 2s none", "2s none, 1s bounce", "2s bounce, 1s all", "2s all, 1s bounce" ],
     invalid_values: [ "2s inherit", "inherit 2s", "2s bounce, 1s inherit", "2s inherit, 1s bounce", "2s initial", "2s all,, 1s bounce", "2s all, , 1s bounce", "bounce 1s cubic-bezier(0, rubbish) 2s", "bounce 1s steps(rubbish) 2s" ]
   },
   "animation-delay": {
     domProp: "animationDelay",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "0s", "0ms" ],
-    other_values: [ "1s", "250ms", "-100ms", "-1s", "1s, 250ms, 2.3s"],
+    other_values: [ "1s", "250ms", "-100ms", "-1s", "1s, 250ms, 2.3s", "calc(1s + 2ms)" ],
     invalid_values: [ "0", "0px" ]
   },
   "animation-direction": {
     domProp: "animationDirection",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "normal" ],
     other_values: [ "alternate", "normal, alternate", "alternate, normal", "normal, normal", "normal, normal, normal", "reverse", "alternate-reverse", "normal, reverse, alternate-reverse, alternate" ],
     invalid_values: [ "normal normal", "inherit, normal", "reverse-alternate" ]
   },
   "animation-duration": {
     domProp: "animationDuration",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "0s", "0ms" ],
-    other_values: [ "1s", "250ms", "1s, 250ms, 2.3s"],
+    other_values: [ "1s", "250ms", "1s, 250ms, 2.3s", "calc(1s + 2ms)" ],
     invalid_values: [ "0", "0px", "-1ms", "-2s" ]
   },
   "animation-fill-mode": {
     domProp: "animationFillMode",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "none" ],
     other_values: [ "forwards", "backwards", "both", "none, none", "forwards, backwards", "forwards, none", "none, both" ],
     invalid_values: [ "all"]
   },
   "animation-iteration-count": {
     domProp: "animationIterationCount",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "1" ],
-    other_values: [ "infinite", "0", "0.5", "7.75", "-0.0", "1, 2, 3", "infinite, 2", "1, infinite" ],
+    other_values: [ "infinite", "0", "0.5", "7.75", "-0.0", "1, 2, 3", "infinite, 2", "1, infinite", "calc(1 + 2.0)" ],
     // negatives forbidden per
     // http://lists.w3.org/Archives/Public/www-style/2011Mar/0355.html
     invalid_values: [ "none", "-1", "-0.5", "-1, infinite", "infinite, -3" ]
   },
   "animation-name": {
     domProp: "animationName",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
@@ -1239,17 +1239,17 @@ var gCSSProperties = {
     other_values: [ "paused", "running, running", "paused, running", "paused, paused", "running, paused", "paused, running, running, running, paused, running" ],
     invalid_values: [ "0" ]
   },
   "animation-timing-function": {
     domProp: "animationTimingFunction",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "ease" ],
-    other_values: [ "cubic-bezier(0.25, 0.1, 0.25, 1.0)", "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)" ],
+    other_values: [ "cubic-bezier(0.25, 0.1, 0.25, 1.0)", "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)", "steps(calc(2 + 1))" ],
     invalid_values: [ "none", "auto", "cubic-bezier(0.25, 0.1, 0.25)", "cubic-bezier(0.25, 0.1, 0.25, 0.25, 1.0)", "cubic-bezier(-0.5, 0.5, 0.5, 0.5)", "cubic-bezier(1.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.5, 0.5, -0.5, 0.5)", "cubic-bezier(0.5, 0.5, 1.5, 0.5)", "steps(2, step-end)", "steps(0)", "steps(-2)", "steps(0, step-end, 1)" ]
   },
   "-moz-appearance": {
     domProp: "MozAppearance",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "none" ],
     other_values: [ "radio", "menulist" ],
--- a/layout/tools/reftest/manifest.jsm
+++ b/layout/tools/reftest/manifest.jsm
@@ -662,19 +662,19 @@ function AddTestItem(aTest, aFilter) {
     if (!aFilter)
         aFilter = [null, [], false];
 
     var {url1, url2} = CreateUrls(Object.assign({}, aTest));
 
     var globalFilter = aFilter[0];
     var manifestFilter = aFilter[1];
     var invertManifest = aFilter[2];
-    if ((globalFilter && !globalFilter.test(url1.spec)) ||
-        (manifestFilter &&
-         !(invertManifest ^ manifestFilter.test(url1.spec))))
+    if (globalFilter && !globalFilter.test(url1.spec))
+        return;
+    if (manifestFilter && !(invertManifest ^ manifestFilter.test(url1.spec)))
         return;
     if (g.focusFilterMode == FOCUS_FILTER_NEEDS_FOCUS_TESTS &&
         !aTest.needsFocus)
         return;
     if (g.focusFilterMode == FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS &&
         aTest.needsFocus)
         return;
 
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -192,17 +192,17 @@ class ReftestResolver(object):
             while not os.path.exists(os.path.join(dirname, default_manifest)):
                 dirname, suffix = os.path.split(dirname)
                 pathname = os.path.join(suffix, pathname)
                 if os.path.dirname(dirname) == dirname:
                     found = False
                     break
             if found:
                 rv = [(os.path.join(dirname, default_manifest),
-                       r".*(?:/|\\)%s(?:[#?].*)?$" % pathname)]
+                       r".*(?:/|\\)%s(?:[#?].*)?$" % pathname.replace('?', '\?'))]
 
         return rv
 
     def absManifestPath(self, path):
         return os.path.normpath(os.path.abspath(path))
 
     def manifestURL(self, options, path):
         return "file://%s" % path
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -26,21 +26,16 @@
 #include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
 #include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
 #include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h"
 #include "webrtc/common_video/include/video_frame_buffer.h"
 #include "webrtc/api/video/i420_buffer.h"
 
 #ifdef WEBRTC_MAC
 #include <AvailabilityMacros.h>
-#endif
-
-#if defined(MAC_OS_X_VERSION_10_8) && \
-  (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8)
-// XXX not available in Mac 10.7 SDK
 #include "webrtc/common_video/include/corevideo_frame_buffer.h"
 #endif
 
 #include "mozilla/Unused.h"
 
 #if defined(MOZ_WIDGET_ANDROID)
 #include "VideoEngine.h"
 #endif
@@ -1902,26 +1897,22 @@ WebrtcVideoConduit::SendVideoFrame(const
     crop_y = (frame.height() - crop_height) / 2;
 
     rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer;
     if (adapted_width == frame.width() && adapted_height == frame.height()) {
       // No adaption - optimized path.
       buffer = frame.video_frame_buffer();
       // XXX Bug 1367651 - Use nativehandles where possible instead of software scaling
 #ifdef WEBRTC_MAC
-#if defined(MAC_OS_X_VERSION_10_8) && \
-  (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8)
-      // XXX not available in Mac 10.7 SDK
       // code adapted from objvideotracksource.mm
     } else if (frame.video_frame_buffer()->native_handle()) {
       // Adapted CVPixelBuffer frame.
       buffer = new rtc::RefCountedObject<webrtc::CoreVideoFrameBuffer>(
         static_cast<CVPixelBufferRef>(frame.video_frame_buffer()->native_handle()), adapted_width, adapted_height,
         crop_width, crop_height, crop_x, crop_y);
-#endif
 #elif WEBRTC_WIN
       // XX FIX
 #elif WEBRTC_LINUX
       // XX FIX
 #elif WEBRTC_ANDROID
       // XX FIX
 #endif
     } else {
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_configuration.mm
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_configuration.mm
@@ -11,26 +11,16 @@
 #include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
 
 #include <math.h>
 #include <algorithm>
 #include <Cocoa/Cocoa.h>
 
 #include "webrtc/system_wrappers/include/logging.h"
 
-#if !defined(MAC_OS_X_VERSION_10_7) || \
-    MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
-
-@interface NSScreen (LionAPI)
-- (CGFloat)backingScaleFactor;
-- (NSRect)convertRectToBacking:(NSRect)aRect;
-@end
-
-#endif  // MAC_OS_X_VERSION_10_7
-
 namespace webrtc {
 
 namespace {
 
 DesktopRect NSRectToDesktopRect(const NSRect& ns_rect) {
   return DesktopRect::MakeLTRB(
       static_cast<int>(floor(ns_rect.origin.x)),
       static_cast<int>(floor(ns_rect.origin.y)),
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mac.mm
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_mac.mm
@@ -11,21 +11,17 @@
 #include <stddef.h>
 
 #include <memory>
 #include <set>
 #include <utility>
 
 #include <ApplicationServices/ApplicationServices.h>
 #include <Cocoa/Cocoa.h>
-#if defined(MAC_OS_X_VERSION_10_8) && \
-  MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
-// 10.8 and above
 #include <CoreGraphics/CoreGraphics.h>
-#endif
 #include <dlfcn.h>
 #include <IOKit/pwr_mgt/IOPMLib.h>
 #include <OpenGL/CGLMacro.h>
 #include <OpenGL/OpenGL.h>
 
 #include "webrtc/base/checks.h"
 #include "webrtc/base/constructormagic.h"
 #include "webrtc/base/macutils.h"
@@ -43,42 +39,37 @@
 #include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
 #include "webrtc/modules/desktop_capture/shared_desktop_frame.h"
 #include "webrtc/system_wrappers/include/logging.h"
 
 // Once Chrome no longer supports OSX 10.8, everything within this
 // preprocessor block can be removed. https://crbug.com/579255
 #if !defined(MAC_OS_X_VERSION_10_9) || \
     MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
-#if defined(MAC_OS_X_VERSION_10_8) && \
-    MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
 CG_EXTERN const CGRect* CGDisplayStreamUpdateGetRects(
     CGDisplayStreamUpdateRef updateRef,
     CGDisplayStreamUpdateRectType rectType,
     size_t* rectCount);
 CG_EXTERN CFRunLoopSourceRef
 CGDisplayStreamGetRunLoopSource(CGDisplayStreamRef displayStream);
 CG_EXTERN CGError CGDisplayStreamStop(CGDisplayStreamRef displayStream);
 CG_EXTERN CGError CGDisplayStreamStart(CGDisplayStreamRef displayStream);
 CG_EXTERN CGDisplayStreamRef
 CGDisplayStreamCreate(CGDirectDisplayID display,
                       size_t outputWidth,
                       size_t outputHeight,
                       int32_t pixelFormat,
                       CFDictionaryRef properties,
                       CGDisplayStreamFrameAvailableHandler handler);
 #endif
-#endif
 
 namespace webrtc {
 
 namespace {
 
-#if defined(MAC_OS_X_VERSION_10_8) && \
-  (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8)
 // CGDisplayStreamRefs need to be destroyed asynchronously after receiving a
 // kCGDisplayStreamFrameStatusStopped callback from CoreGraphics. This may
 // happen after the ScreenCapturerMac has been destroyed. DisplayStreamManager
 // is responsible for destroying all extant CGDisplayStreamRefs, and will
 // destroy itself once it's done.
 class DisplayStreamManager {
  public:
   int GetUniqueId() { return ++unique_id_generator_; }
@@ -135,17 +126,16 @@ class DisplayStreamManager {
     // from CoreGraphics will let us destroy the CGDisplayStreamRef.
     bool active = true;
   };
 
   std::map<int, DisplayStreamWrapper> display_stream_wrappers_;
   int unique_id_generator_ = 0;
   bool ready_for_self_destruction_ = false;
 };
-#endif
 
 // Definitions used to dynamic-link to deprecated OS 10.6 functions.
 const char* kApplicationServicesLibraryName =
     "/System/Library/Frameworks/ApplicationServices.framework/"
     "ApplicationServices";
 typedef void* (*CGDisplayBaseAddressFunc)(CGDirectDisplayID);
 typedef size_t (*CGDisplayBytesPerRowFunc)(CGDirectDisplayID);
 typedef size_t (*CGDisplayBitsPerPixelFunc)(CGDirectDisplayID);
@@ -315,38 +305,16 @@ class ScreenCapturerMac : public Desktop
   void ScreenConfigurationChanged();
 
   bool RegisterRefreshAndMoveHandlers();
   void UnregisterRefreshAndMoveHandlers();
 
   void ScreenRefresh(CGRectCount count, const CGRect *rect_array);
   void ReleaseBuffers();
 
-#if !defined(MAC_OS_X_VERSION_10_8) || \
-  (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8)
-  void ScreenUpdateMove(CGScreenUpdateMoveDelta delta,
-                        size_t count,
-                        const CGRect *rect_array);
-  static void ScreenRefreshCallback(CGRectCount count,
-                                    const CGRect *rect_array,
-                                    void *user_parameter);
-  static void ScreenUpdateMoveCallback(CGScreenUpdateMoveDelta delta,
-                                       size_t count,
-                                       const CGRect *rect_array,
-                                       void *user_parameter);
-  struct ScreenCallbackData {
-    explicit ScreenCallbackData(ScreenCapturerMac* capturer)
-              : capturer(capturer) {}
-    rtc::CriticalSection crit_sect_;
-    ScreenCapturerMac* capturer;
-  };
-
-  ScreenCallbackData* screen_callback_data_;
-#endif
-
   std::unique_ptr<DesktopFrame> CreateFrame();
 
   Callback* callback_ = nullptr;
 
   CGLContextObj cgl_context_ = nullptr;
   ScopedPixelBufferObject pixel_buffer_object_;
 
   // Queue of the frames buffers.
@@ -386,22 +354,26 @@ class ScreenCapturerMac : public Desktop
   CGDisplayBaseAddressFunc cg_display_base_address_ = nullptr;
   CGDisplayBytesPerRowFunc cg_display_bytes_per_row_ = nullptr;
   CGDisplayBitsPerPixelFunc cg_display_bits_per_pixel_ = nullptr;
   void* opengl_library_ = nullptr;
   CGLSetFullScreenFunc cgl_set_full_screen_ = nullptr;
 
   CGWindowID excluded_window_ = 0;
 
-#if defined(MAC_OS_X_VERSION_10_8) && \
-  (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8)
   // A self-owned object that will destroy itself after ScreenCapturerMac and
   // all display streams have been destroyed..
   DisplayStreamManager* display_stream_manager_;
-#endif
+
+  // Used to force CaptureFrame to update it's screen configuration
+  // and reregister event handlers. This ensure that this
+  // occurs on the ScreenCapture thread. Read and written from
+  // both the VideoCapture thread and ScreenCapture thread.
+  // Protected by desktop_config_monitor_.
+  bool update_screen_configuration_ = false;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerMac);
 };
 
 // DesktopFrame wrapper that flips wrapped frame upside down by inverting
 // stride.
 class InvertedDesktopFrame : public DesktopFrame {
  public:
@@ -422,47 +394,43 @@ class InvertedDesktopFrame : public Desk
   std::unique_ptr<DesktopFrame> original_frame_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame);
 };
 
 ScreenCapturerMac::ScreenCapturerMac(
     rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor)
     : desktop_config_monitor_(desktop_config_monitor) {
-#if defined(MAC_OS_X_VERSION_10_8) && \
-  (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8)
   display_stream_manager_ = new DisplayStreamManager;
-#else
-  screen_callback_data_ = new ScreenCallbackData(this);
-#endif
 }
 
 ScreenCapturerMac::~ScreenCapturerMac() {
   ReleaseBuffers();
-#if defined(MAC_OS_X_VERSION_10_8) && \
-  (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8)
   display_stream_manager_->PrepareForSelfDestruction();
-#else
-  {
-    rtc::CritScope lock(&screen_callback_data_->crit_sect_);
-    screen_callback_data_->capturer = nullptr;
-  }
-#endif
   dlclose(app_services_library_);
   dlclose(opengl_library_);
 }
 
 bool ScreenCapturerMac::Init() {
+// MOZILLA: Calling RegisterRefreshAndMoveHandlers here causes us
+// to register on the VideoCapture thread rather than the ScreenCapture
+// thread which will result in us never receiving any notifications.
+// See Bug 1468509.
+/*
   desktop_config_monitor_->Lock();
   desktop_config_ = desktop_config_monitor_->desktop_configuration();
   desktop_config_monitor_->Unlock();
   if (!RegisterRefreshAndMoveHandlers()) {
     return false;
   }
   ScreenConfigurationChanged();
+*/
+  desktop_config_monitor_->Lock();
+  update_screen_configuration_ = true;
+  desktop_config_monitor_->Unlock();
   return true;
 }
 
 void ScreenCapturerMac::ReleaseBuffers() {
   if (cgl_context_) {
     pixel_buffer_object_.Release();
     CGLDestroyContext(cgl_context_);
     cgl_context_ = nullptr;
@@ -473,16 +441,19 @@ void ScreenCapturerMac::ReleaseBuffers()
   queue_.Reset();
 }
 
 void ScreenCapturerMac::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
+  desktop_config_monitor_->Lock();
+  update_screen_configuration_ = true;
+  desktop_config_monitor_->Unlock();
 }
 
 void ScreenCapturerMac::Stop() {
   if (power_assertion_id_display_ != kIOPMNullAssertionID) {
     IOPMAssertionRelease(power_assertion_id_display_);
     power_assertion_id_display_ = kIOPMNullAssertionID;
   }
   if (power_assertion_id_user_ != kIOPMNullAssertionID) {
@@ -491,23 +462,27 @@ void ScreenCapturerMac::Stop() {
   }
 
   callback_ = NULL;
 }
 
 void ScreenCapturerMac::CaptureFrame() {
   int64_t capture_start_time_nanos = rtc::TimeNanos();
 
+  // Spin RunLoop for 1/100th of a second, handling at most one source
+  CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.01, true);
+
   queue_.MoveToNextFrame();
   RTC_DCHECK(!queue_.current_frame() || !queue_.current_frame()->IsShared());
 
   desktop_config_monitor_->Lock();
   MacDesktopConfiguration new_config =
       desktop_config_monitor_->desktop_configuration();
-  if (!desktop_config_.Equals(new_config)) {
+  if (update_screen_configuration_ || !desktop_config_.Equals(new_config)) {
+    update_screen_configuration_ = false;
     desktop_config_ = new_config;
     // If the display configuraiton has changed then refresh capturer data
     // structures. Occasionally, the refresh and move handlers are lost when
     // the screen mode changes, so re-register them here.
     UnregisterRefreshAndMoveHandlers();
     RegisterRefreshAndMoveHandlers();
     ScreenConfigurationChanged();
   }
@@ -992,18 +967,16 @@ void ScreenCapturerMac::ScreenConfigurat
 
   size_t buffer_size = screen_pixel_bounds_.width() *
                        screen_pixel_bounds_.height() *
                        sizeof(uint32_t);
   pixel_buffer_object_.Init(cgl_context_, buffer_size);
 }
 
 bool ScreenCapturerMac::RegisterRefreshAndMoveHandlers() {
-#if defined(MAC_OS_X_VERSION_10_8) && \
-  (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8)
   desktop_config_ = desktop_config_monitor_->desktop_configuration();
   for (const auto& config : desktop_config_.displays) {
     size_t pixel_width = config.pixel_bounds.width();
     size_t pixel_height = config.pixel_bounds.height();
     if (pixel_width == 0 || pixel_height == 0)
       continue;
     // Using a local variable forces the block to capture the raw pointer.
     DisplayStreamManager* manager = display_stream_manager_;
@@ -1042,138 +1015,40 @@ bool ScreenCapturerMac::RegisterRefreshA
         return false;
 
       CFRunLoopSourceRef source =
           CGDisplayStreamGetRunLoopSource(display_stream);
       CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
       display_stream_manager_->SaveStream(unique_id, display_stream);
     }
   }
-#else
- CGError err = CGRegisterScreenRefreshCallback(
-      ScreenCapturerMac::ScreenRefreshCallback, screen_callback_data_);
-  if (err != kCGErrorSuccess) {
-    LOG(LS_ERROR) << "CGRegisterScreenRefreshCallback " << err;
-    return false;
-  }
-
-  err = CGScreenRegisterMoveCallback(
-      ScreenCapturerMac::ScreenUpdateMoveCallback, screen_callback_data_);
-  if (err != kCGErrorSuccess) {
-    LOG(LS_ERROR) << "CGScreenRegisterMoveCallback " << err;
-    return false;
-  }
-#endif
 
   return true;
 }
 
 void ScreenCapturerMac::UnregisterRefreshAndMoveHandlers() {
-#if defined(MAC_OS_X_VERSION_10_8) && \
-  (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8)
   display_stream_manager_->UnregisterActiveStreams();
-#else
-  CGUnregisterScreenRefreshCallback(
-      ScreenCapturerMac::ScreenRefreshCallback, this);
-  CGScreenUnregisterMoveCallback(
-      ScreenCapturerMac::ScreenUpdateMoveCallback, this);
-#endif
 }
 
 void ScreenCapturerMac::ScreenRefresh(CGRectCount count,
                                       const CGRect* rect_array) {
   if (screen_pixel_bounds_.is_empty())
     ScreenConfigurationChanged();
 
   DesktopRegion region;
-#if defined(MAC_OS_X_VERSION_10_8) && \
-  (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8)
   for (CGRectCount i = 0; i < count; ++i) {
     // All rects are already in physical pixel coordinates.
     DesktopRect rect = DesktopRect::MakeXYWH(
         rect_array[i].origin.x, rect_array[i].origin.y,
         rect_array[i].size.width, rect_array[i].size.height);
      region.AddRect(rect);
    }
-#else
-  DesktopVector translate_vector =
-      DesktopVector().subtract(screen_pixel_bounds_.top_left());
-  for (CGRectCount i = 0; i < count; ++i) {
-    // Convert from Density-Independent Pixel to physical pixel coordinates.
-    DesktopRect rect = ScaleAndRoundCGRect(rect_array[i], dip_to_pixel_scale_);
-    // Translate from local desktop to capturer framebuffer coordinates.
-    rect.Translate(translate_vector);
-    region.AddRect(rect);
-  }
-#endif
   helper_.InvalidateRegion(region);
 }
 
-#if !defined(MAC_OS_X_VERSION_10_8) || \
-  (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8)
-void ScreenCapturerMac::ScreenUpdateMove(CGScreenUpdateMoveDelta delta,
-                                         size_t count,
-                                         const CGRect* rect_array) {
-  // Translate |rect_array| to identify the move's destination.
-  CGRect refresh_rects[count];
-  for (CGRectCount i = 0; i < count; ++i) {
-    refresh_rects[i] = CGRectOffset(rect_array[i], delta.dX, delta.dY);
-  }
-
-  // Currently we just treat move events the same as refreshes.
-  ScreenRefresh(count, refresh_rects);
-}
-
-void ScreenCapturerMac::ScreenRefreshCallback(CGRectCount count,
-                                              const CGRect* rect_array,
-                                              void* user_parameter) {
-  ScreenCallbackData* screen_callback_data =
-      reinterpret_cast<ScreenCallbackData*>(user_parameter);
-
-  screen_callback_data->crit_sect_.Enter();
-  if (!screen_callback_data->capturer) {
-    CGUnregisterScreenRefreshCallback(
-        ScreenCapturerMac::ScreenRefreshCallback, screen_callback_data);
-    CGScreenUnregisterMoveCallback(
-        ScreenCapturerMac::ScreenUpdateMoveCallback, screen_callback_data);
-    screen_callback_data->crit_sect_.Leave();
-    delete screen_callback_data;
-    return;
-  }
-
-  if (screen_callback_data->capturer->screen_pixel_bounds_.is_empty())
-    screen_callback_data->capturer->ScreenConfigurationChanged();
-  screen_callback_data->capturer->ScreenRefresh(count, rect_array);
-  screen_callback_data->crit_sect_.Leave();
-}
-
-void ScreenCapturerMac::ScreenUpdateMoveCallback(
-    CGScreenUpdateMoveDelta delta,
-    size_t count,
-    const CGRect* rect_array,
-    void* user_parameter) {
-  ScreenCallbackData* screen_callback_data =
-      reinterpret_cast<ScreenCallbackData*>(user_parameter);
-
-  screen_callback_data->crit_sect_.Enter();
-  if (!screen_callback_data->capturer) {
-    CGUnregisterScreenRefreshCallback(
-        ScreenCapturerMac::ScreenRefreshCallback, screen_callback_data);
-    CGScreenUnregisterMoveCallback(
-        ScreenCapturerMac::ScreenUpdateMoveCallback, screen_callback_data);
-    screen_callback_data->crit_sect_.Leave();
-    delete screen_callback_data;
-    return;
-  }
-
-  screen_callback_data->capturer->ScreenUpdateMove(delta, count, rect_array);
-  screen_callback_data->crit_sect_.Leave();
-}
-#endif
-
 std::unique_ptr<DesktopFrame> ScreenCapturerMac::CreateFrame() {
   std::unique_ptr<DesktopFrame> frame(
       new BasicDesktopFrame(screen_pixel_bounds_.size()));
   frame->set_dpi(DesktopVector(kStandardDPI * dip_to_pixel_scale_,
                                kStandardDPI * dip_to_pixel_scale_));
   return frame;
 }
 
--- a/netwerk/base/nsSimpleURI.cpp
+++ b/netwerk/base/nsSimpleURI.cpp
@@ -507,17 +507,17 @@ nsSimpleURI::GetRef(nsACString &result)
 }
 
 // NOTE: SetRef("") removes our ref, whereas SetRef("#") sets it to the empty
 // string (and will result in .spec and .path having a terminal #).
 nsresult
 nsSimpleURI::SetRef(const nsACString &aRef)
 {
     nsAutoCString ref;
-    nsresult rv = NS_EscapeURL(aRef, esc_OnlyNonASCII, ref, fallible);
+    nsresult rv = NS_EscapeURL(aRef, esc_OnlyNonASCII | esc_Spaces, ref, fallible);
     if (NS_FAILED(rv)) {
         return rv;
     }
 
     if (ref.IsEmpty()) {
         // Empty string means to remove ref completely.
         mIsRefValid = false;
         mRef.Truncate(); // invariant: mRef should be empty when it's not valid
--- a/netwerk/protocol/http/InterceptedHttpChannel.cpp
+++ b/netwerk/protocol/http/InterceptedHttpChannel.cpp
@@ -180,17 +180,17 @@ InterceptedHttpChannel::FollowSyntheticR
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString location;
   rv = mResponseHead->GetHeader(nsHttp::Location, location);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
   // make sure non-ASCII characters in the location header are escaped.
   nsAutoCString locationBuf;
-  if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII, locationBuf)) {
+  if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII | esc_Spaces, locationBuf)) {
     location = locationBuf;
   }
 
   nsCOMPtr<nsIURI> redirectURI;
   rv = ioService->NewURI(nsDependentCString(location.get()),
                          nullptr,
                          mURI,
                          getter_AddRefs(redirectURI));
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -987,17 +987,17 @@ nsHttpChannel::SetupTransaction()
 
     // This is the normal e2e H1 path syntax "/index.html"
     rv = mURI->GetPathQueryRef(path);
     if (NS_FAILED(rv)) {
         return rv;
     }
 
     // path may contain UTF-8 characters, so ensure that they're escaped.
-    if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII, buf)) {
+    if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII | esc_Spaces, buf)) {
         requestURI = &buf;
     } else {
         requestURI = &path;
     }
 
     // trim off the #ref portion if any...
     int32_t ref1 = requestURI->FindChar('#');
     if (ref1 != kNotFound) {
@@ -5498,17 +5498,17 @@ nsHttpChannel::AsyncProcessRedirection(u
 
     // if a location header was not given, then we can't perform the redirect,
     // so just carry on as though this were a normal response.
     if (NS_FAILED(mResponseHead->GetHeader(nsHttp::Location, location)))
         return NS_ERROR_FAILURE;
 
     // make sure non-ASCII characters in the location header are escaped.
     nsAutoCString locationBuf;
-    if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII, locationBuf))
+    if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII | esc_Spaces, locationBuf))
         location = locationBuf;
 
     mRedirectType = redirectType;
 
     LOG(("redirecting to: %s [redirection-limit=%u]\n",
         location.get(), uint32_t(mRedirectionLimit)));
 
     nsresult rv = CreateNewURI(location.get(), getter_AddRefs(mRedirectURI));
--- a/netwerk/protocol/http/nsHttpDigestAuth.cpp
+++ b/netwerk/protocol/http/nsHttpDigestAuth.cpp
@@ -107,17 +107,17 @@ nsHttpDigestAuth::GetMethodAndPath(nsIHt
           //
           // make sure we escape any UTF-8 characters in the URI path.  the
           // digest auth uri attribute needs to match the request-URI.
           //
           // XXX we should really ask the HTTP channel for this string
           // instead of regenerating it here.
           //
           nsAutoCString buf;
-          rv = NS_EscapeURL(path, esc_OnlyNonASCII, buf, mozilla::fallible);
+          rv = NS_EscapeURL(path, esc_OnlyNonASCII | esc_Spaces, buf, mozilla::fallible);
           if (NS_SUCCEEDED(rv)) {
             path = buf;
           }
         }
       }
     }
   }
   return rv;
--- a/netwerk/test/unit/test_URIs.js
+++ b/netwerk/test/unit/test_URIs.js
@@ -48,20 +48,26 @@ var gTests = [
     ref:     "",
     nsIURL:  false, nsINestedURI: false },
   { spec:    "data:text/html;charset=utf-8,<html>\r\n\t</html>",
     scheme:  "data",
     prePath: "data:",
     pathQueryRef: "text/html;charset=utf-8,<html></html>",
     ref:     "",
     nsIURL:  false, nsINestedURI: false },
+  { spec:    "data:text/plain,hello%20world",
+    scheme:  "data",
+    prePath: "data:",
+    pathQueryRef: "text/plain,hello%20world",
+    ref:     "",
+    nsIURL:  false, nsINestedURI: false },
   { spec:    "data:text/plain,hello world",
     scheme:  "data",
     prePath: "data:",
-    pathQueryRef: "text/plain,hello%20world",
+    pathQueryRef: "text/plain,hello world",
     ref:     "",
     nsIURL:  false, nsINestedURI: false },
   { spec:    "file:///dir/afile",
     scheme:  "data",
     prePath: "data:",
     pathQueryRef: "text/plain,2",
     ref:     "",
     relativeURI: "data:te\nxt/plain,2",
@@ -189,17 +195,17 @@ var gTests = [
     scheme:  "mailto",
     prePath: "mailto:",
     pathQueryRef: "webmaster@mozilla.com",
     ref:     "",
     nsIURL:  false, nsINestedURI: false },
   { spec:    "javascript:new Date()",
     scheme:  "javascript",
     prePath: "javascript:",
-    pathQueryRef: "new%20Date()",
+    pathQueryRef: "new Date()",
     ref:     "",
     nsIURL:  false, nsINestedURI: false },
   { spec:    "blob:123456",
     scheme:  "blob",
     prePath: "blob:",
     pathQueryRef: "123456",
     ref:     "",
     nsIURL:  false, nsINestedURI: false, immutable: true },
@@ -583,21 +589,36 @@ function check_nested_mutations()
   uri1 = gIoService.newURI("view-source:about:blank?query#ref");
   uri2 = gIoService.newURI("view-source:about:blank");
   uri3 = uri1.mutate().setPathQueryRef("blank").finalize();
   do_check_uri_eq(uri3, uri2);
   uri3 = uri2.mutate().setPathQueryRef("blank?query#ref").finalize();
   do_check_uri_eq(uri3, uri1);
 }
 
+function check_space_escaping()
+{
+  let uri = gIoService.newURI("data:text/plain,hello%20world#space hash");
+  Assert.equal(uri.spec, "data:text/plain,hello%20world#space%20hash");
+  uri = gIoService.newURI("data:text/plain,hello%20world#space%20hash");
+  Assert.equal(uri.spec, "data:text/plain,hello%20world#space%20hash");
+  uri = gIoService.newURI("data:text/plain,hello world#space%20hash");
+  Assert.equal(uri.spec, "data:text/plain,hello world#space%20hash");
+  uri = gIoService.newURI("data:text/plain,hello world#space hash");
+  Assert.equal(uri.spec, "data:text/plain,hello world#space%20hash");
+  uri = gIoService.newURI("http://example.com/test path#test path");
+  uri = gIoService.newURI("http://example.com/test%20path#test%20path");
+}
+
 // TEST MAIN FUNCTION
 // ------------------
 function run_test()
 {
   check_nested_mutations();
+  check_space_escaping();
 
   // UTF-8 check - From bug 622981
   // ASCII
   let base = gIoService.newURI("http://example.org/xenia?");
   let resolved = gIoService.newURI("?x", null, base);
   let expected = gIoService.newURI("http://example.org/xenia?x");
   do_info("Bug 662981: ACSII - comparing " + resolved.spec + " and " + expected.spec);
   Assert.ok(resolved.equals(expected));
--- a/taskcluster/ci/release-bouncer-sub/kind.yml
+++ b/taskcluster/ci/release-bouncer-sub/kind.yml
@@ -67,9 +67,10 @@ jobs:
       treeherder:
          platform: firefox-release/opt
 
    firefox-rc:
       bouncer-platforms: ['linux', 'linux64', 'osx', 'win', 'win64']
       bouncer-products: ['complete-mar-candidates', 'partial-mar-candidates']
       shipping-product: firefox
       treeherder:
+         symbol: Rel(BncSub-RC)
          platform: firefox-release/opt
--- a/testing/marionette/accessibility.js
+++ b/testing/marionette/accessibility.js
@@ -1,22 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://gre/modules/Log.jsm");
 
-const logger = Log.repository.getLogger("Marionette");
+const {ElementNotAccessibleError} = ChromeUtils.import("chrome://marionette/content/error.js", {});
+const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
 
-const {ElementNotAccessibleError} =
-    ChromeUtils.import("chrome://marionette/content/error.js", {});
+XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
 
 XPCOMUtils.defineLazyGetter(this, "service", () => {
   try {
     return Cc["@mozilla.org/accessibilityService;1"]
         .getService(Ci.nsIAccessibilityService);
   } catch (e) {
     logger.warn("Accessibility module is not present");
     return undefined;
--- a/testing/marionette/components/marionette.js
+++ b/testing/marionette/components/marionette.js
@@ -8,26 +8,22 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const {
   EnvironmentPrefs,
   MarionettePrefs,
 } = ChromeUtils.import("chrome://marionette/content/prefs.js", {});
 
 XPCOMUtils.defineLazyModuleGetters(this, {
-  Log: "resource://gre/modules/Log.jsm",
+  Log: "chrome://marionette/content/log.js",
   Preferences: "resource://gre/modules/Preferences.jsm",
   TCPListener: "chrome://marionette/content/server.js",
 });
 
-XPCOMUtils.defineLazyGetter(this, "log", () => {
-  let log = Log.repository.getLogger("Marionette");
-  log.addAppender(new Log.DumpAppender());
-  return log;
-});
+XPCOMUtils.defineLazyGetter(this, "log", Log.get);
 
 XPCOMUtils.defineLazyServiceGetter(
     this, "env", "@mozilla.org/process/environment;1", "nsIEnvironment");
 
 const NOTIFY_RUNNING = "remote-active";
 
 // Complements -marionette flag for starting the Marionette server.
 // We also set this if Marionette is running in order to start the server
@@ -279,19 +275,16 @@ class MarionetteParentProcess {
     // holds reference to ChromeWindow
     // used to run GFX sanity tests on Windows
     this.gfxWindow = null;
 
     // indicates that all pending window checks have been completed
     // and that we are ready to start the Marionette server
     this.finalUIStartup = false;
 
-    log.level = MarionettePrefs.logLevel;
-    Services.ppmm.initialProcessData["Marionette:Log"] = {level: log.level};
-
     this.enabled = env.exists(ENV_ENABLED);
     this.alteredPrefs = new Set();
 
     Services.prefs.addObserver(PREF_ENABLED, this);
     Services.ppmm.addMessageListener("Marionette:IsRunning", this);
   }
 
   get running() {
new file mode 100644
--- /dev/null
+++ b/testing/marionette/doc/internals/log.rst
@@ -0,0 +1,4 @@
+log module
+==========
+.. js:autoclass:: Log
+  :members:
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -1,16 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 /* global XPCNativeWrapper */
 
-ChromeUtils.import("resource://gre/modules/Log.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.import("chrome://marionette/content/accessibility.js");
 const {Addon} = ChromeUtils.import("chrome://marionette/content/addon.js", {});
 ChromeUtils.import("chrome://marionette/content/assert.js");
 ChromeUtils.import("chrome://marionette/content/atom.js");
 const {
@@ -42,26 +41,28 @@ const {
   UnsupportedOperationError,
   WebDriverError,
 } = ChromeUtils.import("chrome://marionette/content/error.js", {});
 ChromeUtils.import("chrome://marionette/content/evaluate.js");
 const {pprint} = ChromeUtils.import("chrome://marionette/content/format.js", {});
 ChromeUtils.import("chrome://marionette/content/interaction.js");
 ChromeUtils.import("chrome://marionette/content/l10n.js");
 ChromeUtils.import("chrome://marionette/content/legacyaction.js");
+const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
 ChromeUtils.import("chrome://marionette/content/modal.js");
 const {MarionettePrefs} = ChromeUtils.import("chrome://marionette/content/prefs.js", {});
 ChromeUtils.import("chrome://marionette/content/proxy.js");
 ChromeUtils.import("chrome://marionette/content/reftest.js");
 ChromeUtils.import("chrome://marionette/content/session.js");
 const {
   PollPromise,
   TimedPromise,
 } = ChromeUtils.import("chrome://marionette/content/sync.js", {});
 
+XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
 XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 this.EXPORTED_SYMBOLS = ["GeckoDriver"];
 
 const APP_ID_FIREFOX = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
 
 const FRAME_SCRIPT = "chrome://marionette/content/listener.js";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
@@ -71,17 +72,16 @@ const SUPPORTED_STRATEGIES = new Set([
   element.Strategy.Selector,
   element.Strategy.ID,
   element.Strategy.TagName,
   element.Strategy.XPath,
   element.Strategy.Anon,
   element.Strategy.AnonAttribute,
 ]);
 
-const logger = Log.repository.getLogger("Marionette");
 const globalMessageManager = Services.mm;
 
 /**
  * The Marionette WebDriver services provides a standard conforming
  * implementation of the W3C WebDriver specification.
  *
  * @see {@link https://w3c.github.io/webdriver/webdriver-spec.html}
  * @namespace driver
--- a/testing/marionette/evaluate.js
+++ b/testing/marionette/evaluate.js
@@ -1,30 +1,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-ChromeUtils.import("resource://gre/modules/Log.jsm");
 ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.import("chrome://marionette/content/assert.js");
 const {
   element,
   WebElement,
 } = ChromeUtils.import("chrome://marionette/content/element.js", {});
 const {
   JavaScriptError,
   ScriptTimeoutError,
 } = ChromeUtils.import("chrome://marionette/content/error.js", {});
+const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
 
-const log = Log.repository.getLogger("Marionette");
+XPCOMUtils.defineLazyGetter(this, "log", Log.get);
 
 this.EXPORTED_SYMBOLS = ["evaluate", "sandbox", "Sandboxes"];
 
 const ARGUMENTS = "__webDriverArguments";
 const CALLBACK = "__webDriverCallback";
 const COMPLETE = "__webDriverComplete";
 const DEFAULT_TIMEOUT = 10000; // ms
 const FINISH = "finish";
--- a/testing/marionette/format.js
+++ b/testing/marionette/format.js
@@ -1,20 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-ChromeUtils.import("resource://gre/modules/Log.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
+
+XPCOMUtils.defineLazyGetter(this, "log", Log.get);
 
 this.EXPORTED_SYMBOLS = ["pprint", "truncate"];
 
-const log = Log.repository.getLogger("Marionette");
-
 const MAX_STRING_LENGTH = 250;
 
 /**
  * Pretty-print values passed to template strings.
  *
  * Usage:
  *
  * <pre><code>
--- a/testing/marionette/jar.mn
+++ b/testing/marionette/jar.mn
@@ -19,16 +19,17 @@ marionette.jar:
   content/error.js (error.js)
   content/evaluate.js (evaluate.js)
   content/event.js  (event.js)
   content/format.js (format.js)
   content/interaction.js (interaction.js)
   content/l10n.js (l10n.js)
   content/legacyaction.js (legacyaction.js)
   content/listener.js (listener.js)
+  content/log.js (log.js)
   content/message.js (message.js)
   content/modal.js (modal.js)
   content/navigate.js (navigate.js)
   content/packets.js (packets.js)
   content/prefs.js (prefs.js)
   content/proxy.js (proxy.js)
   content/reftest.js (reftest.js)
   content/reftest.xul (reftest.xul)
--- a/testing/marionette/legacyaction.js
+++ b/testing/marionette/legacyaction.js
@@ -1,29 +1,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-ChromeUtils.import("resource://gre/modules/Log.jsm");
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const {
   element,
   WebElement,
 } = ChromeUtils.import("chrome://marionette/content/element.js", {});
 ChromeUtils.import("chrome://marionette/content/evaluate.js");
 ChromeUtils.import("chrome://marionette/content/event.js");
+const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
+
+XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
 
 const CONTEXT_MENU_DELAY_PREF = "ui.click_hold_context_menus.delay";
 const DEFAULT_CONTEXT_MENU_DELAY = 750;  // ms
 
 this.EXPORTED_SYMBOLS = ["legacyaction"];
 
-const logger = Log.repository.getLogger("Marionette");
-
 /** @namespace */
 this.legacyaction = this.action = {};
 
 /**
  * Functionality for (single finger) action chains.
  */
 action.Chain = function() {
   // for assigning unique ids to all touches
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -6,17 +6,16 @@
 /* global XPCNativeWrapper */
 
 "use strict";
 
 const winUtil = content.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIDOMWindowUtils);
 
 ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
-ChromeUtils.import("resource://gre/modules/Log.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.import("chrome://marionette/content/accessibility.js");
 ChromeUtils.import("chrome://marionette/content/action.js");
 ChromeUtils.import("chrome://marionette/content/atom.js");
 ChromeUtils.import("chrome://marionette/content/capture.js");
 const {
@@ -34,22 +33,25 @@ const {
   UnknownError,
 } = ChromeUtils.import("chrome://marionette/content/error.js", {});
 ChromeUtils.import("chrome://marionette/content/evaluate.js");
 ChromeUtils.import("chrome://marionette/content/event.js");
 const {ContentEventObserverService} = ChromeUtils.import("chrome://marionette/content/dom.js", {});
 const {pprint, truncate} = ChromeUtils.import("chrome://marionette/content/format.js", {});
 ChromeUtils.import("chrome://marionette/content/interaction.js");
 ChromeUtils.import("chrome://marionette/content/legacyaction.js");
+const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
 ChromeUtils.import("chrome://marionette/content/navigate.js");
 ChromeUtils.import("chrome://marionette/content/proxy.js");
 ChromeUtils.import("chrome://marionette/content/session.js");
 
+XPCOMUtils.defineLazyGetter(this, "logger", () => Log.getWithPrefix(outerWindowID));
 XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
+let {outerWindowID} = winUtil;
 let curContainer = {frame: content, shadowRoot: null};
 
 // Listen for click event to indicate one click has happened, so actions
 // code can send dblclick event
 addEventListener("click", event.DoubleClickTracker.setClick);
 addEventListener("dblclick", event.DoubleClickTracker.resetClick);
 addEventListener("unload", event.DoubleClickTracker.resetClick, true);
 
@@ -73,25 +75,16 @@ Object.defineProperty(this, "capabilitie
   configurable: true,
 });
 
 let legacyactions = new legacyaction.Chain();
 
 // last touch for each fingerId
 let multiLast = {};
 
-// TODO: Log.jsm is not e10s compatible (see https://bugzil.la/1411513),
-// query the parent process for the current log level
-const logger = Log.repository.getLogger("Marionette");
-if (logger.ownAppenders.length == 0) {
-  let log = Services.cpmm.initialProcessData["Marionette:Log"];
-  logger.level = log.level;
-  logger.addAppender(new Log.DumpAppender());
-}
-
 // sandbox storage and name of the current sandbox
 const sandboxes = new Sandboxes(() => curContainer.frame);
 
 const eventObservers = new ContentEventObserverService(
     content, sendAsyncMessage.bind(this));
 
 /**
  * The load listener singleton helps to keep track of active page load
@@ -440,34 +433,34 @@ const loadListener = {
 };
 
 /**
  * Called when listener is first started up.  The listener sends its
  * unique window ID and its current URI to the actor.  If the actor returns
  * an ID, we start the listeners. Otherwise, nothing happens.
  */
 function registerSelf() {
-  let {outerWindowID} = winUtil;
-  logger.debug(`Register listener.js for window ${outerWindowID}`);
+  logger.debug("Frame script loaded");
 
   sandboxes.clear();
   curContainer = {
     frame: content,
     shadowRoot: null,
   };
   legacyactions.mouseEventsOnly = false;
   action.inputStateMap = new Map();
   action.inputsToCancel = [];
 
   let reply = sendSyncMessage("Marionette:Register", {outerWindowID});
   if (reply.length == 0) {
     logger.error("No reply from Marionette:Register");
   }
 
   if (reply[0].outerWindowID === outerWindowID) {
+    logger.debug("Frame script registered");
     startListeners();
     sendAsyncMessage("Marionette:ListenersAttached", {outerWindowID});
   }
 }
 
 // Eventually we will not have a closure for every single command,
 // but use a generic dispatch for all listener commands.
 //
@@ -1584,47 +1577,47 @@ function flushRendering() {
     let afterPaintWasPending = utils.isMozAfterPaintPending;
 
     let root = win.document.documentElement;
     if (root) {
       try {
         // Flush pending restyles and reflows for this window
         root.getBoundingClientRect();
       } catch (e) {
-        logger.warning(`flushWindow failed: ${e}`);
+        logger.error("flushWindow failed", e);
       }
     }
 
     if (!afterPaintWasPending && utils.isMozAfterPaintPending) {
       anyPendingPaintsGeneratedInDescendants = true;
     }
 
     for (let i = 0; i < win.frames.length; ++i) {
       flushWindow(win.frames[i]);
     }
   }
   flushWindow(content);
 
   if (anyPendingPaintsGeneratedInDescendants &&
       !windowUtils.isMozAfterPaintPending) {
-    logger.error("Internal error: descendant frame generated a MozAfterPaint event, but the root document doesn't have one!");
+    logger.error("Descendant frame generated a MozAfterPaint event, " +
+        "but the root document doesn't have one!");
   }
 
   logger.debug(`flushRendering ${windowUtils.isMozAfterPaintPending}`);
   return windowUtils.isMozAfterPaintPending;
 }
 
 async function reftestWait(url, remote) {
   let win = curContainer.frame;
   let document = curContainer.frame.document;
 
   let windowUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIDOMWindowUtils);
 
-
   let reftestWait = false;
 
   if (document.location.href !== url || document.readyState != "complete") {
     logger.debug(truncate`Waiting for page load of ${url}`);
     await new Promise(resolve => {
       let maybeResolve = event => {
         if (event.target === curContainer.frame.document &&
             event.target.location.href === url) {
new file mode 100644
--- /dev/null
+++ b/testing/marionette/log.js
@@ -0,0 +1,94 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+const StdLog = ChromeUtils.import("resource://gre/modules/Log.jsm", {}).Log;
+
+const {MarionettePrefs} = ChromeUtils.import("chrome://marionette/content/prefs.js", {});
+
+this.EXPORTED_SYMBOLS = ["Log"];
+
+const isChildProcess = Services.appinfo.processType ==
+    Services.appinfo.PROCESS_TYPE_CONTENT;
+
+/**
+ * Shorthand for accessing the Marionette logging repository.
+ *
+ * Using this class to retrieve the `Log.jsm` repository for
+ * Marionette will ensure the logger is set up correctly with the
+ * appropriate stdout dumper and with the correct log level.
+ *
+ * Unlike `Log.jsm` this logger is E10s safe, meaning repository
+ * configuration for appenders and logging levels are communicated
+ * across processes.
+ *
+ * @name Log
+ */
+class MarionetteLog {
+  /**
+   * Obtain the `Marionette` logger.
+   *
+   * The returned {@link Logger} instance is shared among all
+   * callers in the same process.
+   *
+   * @return {Logger}
+   */
+  static get() {
+    let logger = StdLog.repository.getLogger("Marionette");
+    if (logger.ownAppenders.length == 0) {
+      logger.addAppender(new StdLog.DumpAppender());
+    }
+    logger.level = MarionettePrefs.logLevel;
+    return logger;
+  }
+
+  /**
+   * Obtain a logger that logs all messages with a prefix.
+   *
+   * Unlike {@link LoggerRepository.getLoggerWithMessagePrefix()}
+   * this function will ensure invoke {@link #get()} first to ensure
+   * the logger has been properly set up first.
+   *
+   * This returns a new object with a prototype chain that chains
+   * up the original {@link Logger} instance.  The new prototype has
+   * log functions that prefix `prefix` to each message.
+   *
+   * @param {string} prefix
+   *     String to prefix each logged message with.
+   *
+   * @return {Proxy.<Logger>}
+   */
+  static getWithPrefix(prefix) {
+    this.get();
+    return StdLog.repository.getLoggerWithMessagePrefix("Marionette", `[${prefix}] `);
+  }
+}
+
+class ParentProcessLog extends MarionetteLog {
+  static get() {
+    let logger = super.get();
+    Services.ppmm.initialProcessData["Marionette:Log"] = {level: logger.level};
+    return logger;
+  }
+}
+
+class ChildProcessLog extends MarionetteLog {
+  static get() {
+    let logger = super.get();
+
+    // Log.jsm is not e10s compatible (see https://bugzil.la/1411513)
+    // so loading it in a new child process will reset the repository config
+    logger.level = Services.cpmm.initialProcessData["Marionette:Log"] || StdLog.Level.Info;
+
+    return logger;
+  }
+}
+
+if (isChildProcess) {
+  this.Log = ChildProcessLog;
+} else {
+  this.Log = ParentProcessLog;
+}
--- a/testing/marionette/prefs.js
+++ b/testing/marionette/prefs.js
@@ -121,24 +121,16 @@ class Branch {
  * potentially malformed input.  In the `marionette.log.level` example,
  * `DEBUG`, `Debug`, and `dEbUg` are considered valid inputs and the
  * `LogBranch` specialisation deserialises the string value to the
  * correct `Log.Level` by sanitising the input data first.
  *
  * A further complication is that we cannot rely on `Preferences.jsm`
  * in Marionette.  See https://bugzilla.mozilla.org/show_bug.cgi?id=1357517
  * for further details.
- *
- * Usage::
- *
- *     ChromeUtils.import("resource://gre/modules/Log.jsm");
- *     const {MarionettePrefs} = ChromeUtils.import("chrome://marionette/content/prefs.js", {});
- *
- *     const log = Log.repository.getLogger("Marionette");
- *     log.level = MarionettePrefs.log.level;
  */
 class MarionetteBranch extends Branch {
   constructor(branch = "marionette.") {
     super(branch);
   }
 
   /**
    * The `marionette.enabled` preference.  When it returns true,
--- a/testing/marionette/proxy.js
+++ b/testing/marionette/proxy.js
@@ -1,35 +1,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-ChromeUtils.import("resource://gre/modules/Log.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const {
   error,
   WebDriverError,
 } = ChromeUtils.import("chrome://marionette/content/error.js", {});
 ChromeUtils.import("chrome://marionette/content/evaluate.js");
+const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
 ChromeUtils.import("chrome://marionette/content/modal.js");
 const {
   MessageManagerDestroyedPromise,
 } = ChromeUtils.import("chrome://marionette/content/sync.js", {});
 
 this.EXPORTED_SYMBOLS = ["proxy"];
 
+XPCOMUtils.defineLazyGetter(this, "log", Log.get);
 XPCOMUtils.defineLazyServiceGetter(
     this, "uuidgen", "@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");
 
-const log = Log.repository.getLogger("Marionette");
-
 // Proxy handler that traps requests to get a property.  Will prioritise
 // properties that exist on the object's own prototype.
 const ownPriorityGetterTrap = {
   get: (obj, prop) => {
     if (obj.hasOwnProperty(prop)) {
       return obj[prop];
     }
     return (...args) => obj.send(prop, args);
--- a/testing/marionette/reftest.js
+++ b/testing/marionette/reftest.js
@@ -1,29 +1,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-ChromeUtils.import("resource://gre/modules/Log.jsm");
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.import("chrome://marionette/content/assert.js");
 ChromeUtils.import("chrome://marionette/content/capture.js");
-const {InvalidArgumentError} =
-    ChromeUtils.import("chrome://marionette/content/error.js", {});
+const {InvalidArgumentError} = ChromeUtils.import("chrome://marionette/content/error.js", {});
+const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
+
+XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
 
 this.EXPORTED_SYMBOLS = ["reftest"];
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const PREF_E10S = "browser.tabs.remote.autostart";
 
-const logger = Log.repository.getLogger("Marionette");
-
 const SCREENSHOT_MODE = {
   unexpected: 0,
   fail: 1,
   always: 2,
 };
 
 const STATUS = {
   PASS: "PASS",
--- a/testing/marionette/server.js
+++ b/testing/marionette/server.js
@@ -6,35 +6,36 @@
 
 const CC = Components.Constructor;
 
 const ServerSocket = CC(
     "@mozilla.org/network/server-socket;1",
     "nsIServerSocket",
     "initSpecialConnection");
 
-ChromeUtils.import("resource://gre/modules/Log.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.import("chrome://marionette/content/assert.js");
 const {GeckoDriver} = ChromeUtils.import("chrome://marionette/content/driver.js", {});
 const {WebElement} = ChromeUtils.import("chrome://marionette/content/element.js", {});
 const {
   error,
   UnknownCommandError,
 } = ChromeUtils.import("chrome://marionette/content/error.js", {});
 const {
   Command,
   Message,
   Response,
 } = ChromeUtils.import("chrome://marionette/content/message.js", {});
+const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
 const {MarionettePrefs} = ChromeUtils.import("chrome://marionette/content/prefs.js", {});
 const {DebuggerTransport} = ChromeUtils.import("chrome://marionette/content/transport.js", {});
 
-const logger = Log.repository.getLogger("Marionette");
+XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
 
 const {KeepWhenOffline, LoopbackOnly} = Ci.nsIServerSocket;
 
 this.EXPORTED_SYMBOLS = [
   "TCPConnection",
   "TCPListener",
 ];
 
--- a/testing/marionette/sync.js
+++ b/testing/marionette/sync.js
@@ -1,33 +1,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-ChromeUtils.import("resource://gre/modules/Log.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const {
   error,
   TimeoutError,
 } = ChromeUtils.import("chrome://marionette/content/error.js", {});
+const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
+
+XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
 
 this.EXPORTED_SYMBOLS = [
   /* exported PollPromise, TimedPromise */
   "PollPromise",
   "TimedPromise",
 
   /* exported MessageManagerDestroyedPromise */
   "MessageManagerDestroyedPromise",
 ];
 
-const logger = Log.repository.getLogger("Marionette");
-
 const {TYPE_ONE_SHOT, TYPE_REPEATING_SLACK} = Ci.nsITimer;
 
 /**
  * @callback Condition
  *
  * @param {function(*)} resolve
  *     To be called when the condition has been met.  Will return the
  *     resolved value.
--- a/testing/mochitest/BrowserTestUtils/ContentTaskUtils.jsm
+++ b/testing/mochitest/BrowserTestUtils/ContentTaskUtils.jsm
@@ -20,20 +20,24 @@ ChromeUtils.import("resource://gre/modul
 
 var ContentTaskUtils = {
   /**
    * Will poll a condition function until it returns true.
    *
    * @param condition
    *        A condition function that must return true or false. If the
    *        condition ever throws, this is also treated as a false.
+   * @param msg
+   *        The message to use when the returned promise is rejected.
+   *        This message will be extended with additional information
+   *        about the number of tries or the thrown exception.
    * @param interval
    *        The time interval to poll the condition function. Defaults
    *        to 100ms.
-   * @param attempts
+   * @param maxTries
    *        The number of times to poll before giving up and rejecting
    *        if the condition has not yet returned true. Defaults to 50
    *        (~5 seconds for 100ms intervals)
    * @return Promise
    *        Resolves when condition is true.
    *        Rejects if timeout is exceeded or condition ever throws.
    */
   waitForCondition(condition, msg, interval = 100, maxTries = 50) {
--- a/testing/web-platform/meta/fetch/data-urls/processing.any.js.ini
+++ b/testing/web-platform/meta/fetch/data-urls/processing.any.js.ini
@@ -1,18 +1,15 @@
 [processing.any.html]
   ["data://test/,X"]
     expected: FAIL
 
   ["data://test:test/,X"]
     expected: FAIL
 
-  ["data:text/plain ,X"]
-    expected: FAIL
-
   ["data:;x=x;charset=x,X"]
     expected: FAIL
 
   ["data:;x=x,X"]
     expected: FAIL
 
   ["data:IMAGE/gif;hi=x,%C2%B1"]
     expected: FAIL
@@ -24,19 +21,16 @@
     expected: FAIL
 
   ["data:\\0,%FF"]
     expected: FAIL
 
   ["data:%00,%FF"]
     expected: FAIL
 
-  ["data:text/html  ,X"]
-    expected: FAIL
-
   ["data:text / html,X"]
     expected: FAIL
 
   ["data:†,X"]
     expected: FAIL
 
   ["data:X,X"]
     expected: FAIL
@@ -110,19 +104,16 @@
 
 [processing.any.worker.html]
   ["data://test/,X"]
     expected: FAIL
 
   ["data://test:test/,X"]
     expected: FAIL
 
-  ["data:text/plain ,X"]
-    expected: FAIL
-
   ["data:;x=x;charset=x,X"]
     expected: FAIL
 
   ["data:;x=x,X"]
     expected: FAIL
 
   ["data:IMAGE/gif;hi=x,%C2%B1"]
     expected: FAIL
@@ -134,19 +125,16 @@
     expected: FAIL
 
   ["data:\\0,%FF"]
     expected: FAIL
 
   ["data:%00,%FF"]
     expected: FAIL
 
-  ["data:text/html  ,X"]
-    expected: FAIL
-
   ["data:text / html,X"]
     expected: FAIL
 
   ["data:†,X"]
     expected: FAIL
 
   ["data:X,X"]
     expected: FAIL
--- a/testing/web-platform/meta/url/url-constructor.html.ini
+++ b/testing/web-platform/meta/url/url-constructor.html.ini
@@ -1,12 +1,9 @@
 [url-constructor.html]
-  [Parsing: <a:\t foo.com> against <http://example.org/foo/bar>]
-    expected: FAIL
-
   [Parsing: <foo://> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <http::@c:29> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <http://::@c@d:2> against <http://example.org/foo/bar>]
     expected: FAIL
@@ -210,19 +207,16 @@
     expected: FAIL
 
   [Parsing: <http://256.256.256.256> against <http://other.com/>]
     expected: FAIL
 
   [Parsing: <..> against <file:///C:/>]
     expected: FAIL
 
-  [Parsing: <lolscheme:x x#x x> against <about:blank>]
-    expected: FAIL
-
   [Parsing: <file://example:1/> against <about:blank>]
     expected: FAIL
 
   [Parsing: <file://example:test/> against <about:blank>]
     expected: FAIL
 
   [Parsing: <file://example%/> against <about:blank>]
     expected: FAIL
--- a/xpcom/io/nsEscape.cpp
+++ b/xpcom/io/nsEscape.cpp
@@ -314,16 +314,17 @@ T_EscapeURL(const typename T::char_type*
     return NS_ERROR_INVALID_ARG;
   }
 
   bool forced = !!(aFlags & esc_Forced);
   bool ignoreNonAscii = !!(aFlags & esc_OnlyASCII);
   bool ignoreAscii = !!(aFlags & esc_OnlyNonASCII);
   bool writing = !!(aFlags & esc_AlwaysCopy);
   bool colon = !!(aFlags & esc_Colon);
+  bool spaces = !!(aFlags & esc_Spaces);
 
   auto src = reinterpret_cast<const unsigned_char_type*>(aPart);
 
   typename T::char_type tempBuffer[100];
   unsigned int tempBufferPos = 0;
 
   bool previousIsNonASCII = false;
   for (size_t i = 0; i < aPartLen; ++i) {
@@ -351,22 +352,22 @@ T_EscapeURL(const typename T::char_type*
     // And, we will not escape non-ascii characters if requested.
     // On special request we will also escape the colon even when
     // not covered by the matrix.
     // ignoreAscii is not honored for control characters (C0 and DEL)
     //
     // And, we should escape the '|' character when it occurs after any
     // non-ASCII character as it may be aPart of a multi-byte character.
     //
-    // 0x20..0x7e are the valid ASCII characters. We also escape spaces
-    // (0x20) since they are not legal in URLs.
+    // 0x20..0x7e are the valid ASCII characters.
     if ((dontNeedEscape(c, aFlags) || (c == HEX_ESCAPE && !forced)
          || (c > 0x7f && ignoreNonAscii)
-         || (c > 0x20 && c < 0x7f && ignoreAscii))
+         || (c >= 0x20 && c < 0x7f && ignoreAscii))
         && !(c == ':' && colon)
+        && !(c == ' ' && spaces)
         && !(previousIsNonASCII && c == '|' && !ignoreNonAscii)) {
       if (writing) {
         tempBuffer[tempBufferPos++] = c;
       }
     } else { /* do the escape magic */
       if (!writing) {
         if (!aResult.Append(aPart, i, mozilla::fallible)) {
           return NS_ERROR_OUT_OF_MEMORY;
--- a/xpcom/io/nsEscape.h
+++ b/xpcom/io/nsEscape.h
@@ -88,17 +88,18 @@ enum EscapeMask {
   esc_Minimal        = esc_Scheme | esc_Username | esc_Password | esc_Host | esc_FilePath | esc_Param | esc_Query | esc_Ref,
   esc_Forced         = 1u << 10, /* forces escaping of existing escape sequences */
   esc_OnlyASCII      = 1u << 11, /* causes non-ascii octets to be skipped */
   esc_OnlyNonASCII   = 1u << 12, /* causes _graphic_ ascii octets (0x20-0x7E)
                                     * to be skipped when escaping. causes all
                                     * ascii octets (<= 0x7F) to be skipped when unescaping */
   esc_AlwaysCopy     = 1u << 13, /* copy input to result buf even if escaping is unnecessary */
   esc_Colon          = 1u << 14, /* forces escape of colon */
-  esc_SkipControl    = 1u << 15  /* skips C0 and DEL from unescaping */
+  esc_SkipControl    = 1u << 15, /* skips C0 and DEL from unescaping */
+  esc_Spaces         = 1u << 16  /* forces escape of spaces */
 };
 
 /**
  * NS_EscapeURL
  *
  * Escapes invalid char's in an URL segment.  Has no side-effect if the URL
  * segment is already escaped, unless aFlags has the esc_Forced bit in which
  * case % will also be escaped.  Iff some part of aStr is escaped is the
--- a/xpcom/tests/gtest/TestEscape.cpp
+++ b/xpcom/tests/gtest/TestEscape.cpp
@@ -128,8 +128,24 @@ TEST(Escape, nsAppendEscapedHTML)
   nsCString dst;
   for (size_t i = 0; i < ArrayLength(srcs); i++) {
     nsCString src(srcs[i]);
     nsAppendEscapedHTML(src, dst);
     ASSERT_TRUE(dst.Equals(dsts2[i]));
   }
 }
 
+TEST(Escape, EscapeSpaces)
+{
+  // Tests the fallible version of NS_EscapeURL works as expected when no
+  // escaping is necessary.
+  nsCString toEscape("data:\x0D\x0A spa ces\xC4\x9F");
+  nsCString escaped;
+  nsresult rv = NS_EscapeURL(toEscape, esc_OnlyNonASCII, escaped, fallible);
+  EXPECT_EQ(rv, NS_OK);
+  // Only non-ASCII and C0
+  EXPECT_STREQ(escaped.BeginReading(), "data:%0D%0A spa ces%C4%9F");
+
+  escaped.Truncate();
+  rv = NS_EscapeURL(toEscape, esc_OnlyNonASCII | esc_Spaces, escaped, fallible);
+  EXPECT_EQ(rv, NS_OK);
+  EXPECT_STREQ(escaped.BeginReading(), "data:%0D%0A%20spa%20ces%C4%9F");
+}