Bug 1069401 - UserAgent cannot be changed for specific websites in workers, r=khuey, r=bz, a=sledru
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 24 Sep 2014 17:09:50 +0100
changeset 216851 e178848e43d1
parent 216850 6d53cfba12f0
child 216852 12a5b8d685b2
push id3941
push userryanvm@gmail.com
push date2014-09-25 21:01 +0000
treeherdermozilla-beta@e178848e43d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, bz, sledru
bugs1069401
milestone33.0
Bug 1069401 - UserAgent cannot be changed for specific websites in workers, r=khuey, r=bz, a=sledru
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/base/nsGlobalWindow.cpp
dom/base/test/mochitest.ini
dom/base/test/test_navigatorPrefOverride.html
dom/webidl/Navigator.webidl
dom/workers/Navigator.cpp
dom/workers/Navigator.h
dom/workers/RuntimeService.cpp
dom/workers/RuntimeService.h
dom/workers/test/test_bug1062920.html
dom/workers/test/test_bug1062920.xul
netwerk/test/mochitests/test_user_agent_overrides.html
netwerk/test/mochitests/test_user_agent_updates.html
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -308,40 +308,29 @@ Navigator::Invalidate()
 
 //*****************************************************************************
 //    Navigator::nsIDOMNavigator
 //*****************************************************************************
 
 NS_IMETHODIMP
 Navigator::GetUserAgent(nsAString& aUserAgent)
 {
-  nsresult rv = NS_GetNavigatorUserAgent(aUserAgent);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!mWindow || !mWindow->GetDocShell()) {
-    return NS_OK;
-  }
-
-  nsIDocument* doc = mWindow->GetExtantDoc();
-  if (!doc) {
-    return NS_OK;
+  nsCOMPtr<nsIURI> codebaseURI;
+  nsCOMPtr<nsPIDOMWindow> window;
+
+  if (mWindow && mWindow->GetDocShell()) {
+    window = mWindow;
+    nsIDocument* doc = mWindow->GetExtantDoc();
+    if (doc) {
+      doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
+    }
   }
 
-  nsCOMPtr<nsIURI> codebaseURI;
-  doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
-  if (!codebaseURI) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsISiteSpecificUserAgent> siteSpecificUA =
-    do_GetService("@mozilla.org/dom/site-specific-user-agent;1");
-  NS_ENSURE_TRUE(siteSpecificUA, NS_OK);
-
-  return siteSpecificUA->GetUserAgentForURIAndWindow(codebaseURI, mWindow,
-                                                     aUserAgent);
+  return GetUserAgent(window, codebaseURI, nsContentUtils::IsCallerChrome(),
+                      aUserAgent);
 }
 
 NS_IMETHODIMP
 Navigator::GetAppCodeName(nsAString& aAppCodeName)
 {
   nsresult rv;
 
   nsCOMPtr<nsIHttpProtocolHandler>
@@ -2425,16 +2414,18 @@ Navigator::GetPlatform(nsAString& aPlatf
 #endif
 
   return rv;
 }
 
 /* static */ nsresult
 Navigator::GetAppVersion(nsAString& aAppVersion, bool aUsePrefOverriddenValue)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   if (aUsePrefOverriddenValue && !nsContentUtils::IsCallerChrome()) {
     const nsAdoptingString& override =
       mozilla::Preferences::GetString("general.appversion.override");
 
     if (override) {
       aAppVersion = override;
       return NS_OK;
     }
@@ -2460,39 +2451,72 @@ Navigator::GetAppVersion(nsAString& aApp
   aAppVersion.Append(char16_t(')'));
 
   return rv;
 }
 
 /* static */ void
 Navigator::AppName(nsAString& aAppName, bool aUsePrefOverriddenValue)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   if (aUsePrefOverriddenValue && !nsContentUtils::IsCallerChrome()) {
     const nsAdoptingString& override =
       mozilla::Preferences::GetString("general.appname.override");
 
     if (override) {
       aAppName = override;
       return;
     }
   }
 
   aAppName.AssignLiteral("Netscape");
 }
 
-} // namespace dom
-} // namespace mozilla
-
 nsresult
-NS_GetNavigatorUserAgent(nsAString& aUserAgent)
+Navigator::GetUserAgent(nsPIDOMWindow* aWindow, nsIURI* aURI,
+                        bool aIsCallerChrome,
+                        nsAString& aUserAgent)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!aIsCallerChrome) {
+    const nsAdoptingString& override =
+      mozilla::Preferences::GetString("general.useragent.override");
+
+    if (override) {
+      aUserAgent = override;
+      return NS_OK;
+    }
+  }
+
   nsresult rv;
-
   nsCOMPtr<nsIHttpProtocolHandler>
     service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   nsAutoCString ua;
   rv = service->GetUserAgent(ua);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   CopyASCIItoUTF16(ua, aUserAgent);
 
-  return rv;
+  if (!aWindow || !aURI) {
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(aWindow->GetDocShell());
+
+  nsCOMPtr<nsISiteSpecificUserAgent> siteSpecificUA =
+    do_GetService("@mozilla.org/dom/site-specific-user-agent;1");
+  if (!siteSpecificUA) {
+    return NS_OK;
+  }
+
+  return siteSpecificUA->GetUserAgentForURIAndWindow(aURI, aWindow, aUserAgent);
 }
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -22,16 +22,17 @@
 class nsPluginArray;
 class nsMimeTypeArray;
 class nsPIDOMWindow;
 class nsIDOMNavigatorSystemMessages;
 class nsDOMCameraManager;
 class nsDOMDeviceStorage;
 class nsIDOMBlob;
 class nsIPrincipal;
+class nsIURI;
 
 namespace mozilla {
 namespace dom {
 class Geolocation;
 class systemMessageCallback;
 struct MediaStreamConstraints;
 class WakeLock;
 class ArrayBufferViewOrBlobOrStringOrFormData;
@@ -164,16 +165,21 @@ public:
   static void AppName(nsAString& aAppName, bool aUsePrefOverriddenValue);
 
   static nsresult GetPlatform(nsAString& aPlatform,
                               bool aUsePrefOverriddenValue);
 
   static nsresult GetAppVersion(nsAString& aAppVersion,
                                 bool aUsePrefOverriddenValue);
 
+  static nsresult GetUserAgent(nsPIDOMWindow* aWindow,
+                               nsIURI* aURI,
+                               bool aIsCallerChrome,
+                               nsAString& aUserAgent);
+
   already_AddRefed<Promise> GetDataStores(const nsAString &aName,
                                           ErrorResult& aRv);
 
   // Feature Detection API
   already_AddRefed<Promise> GetFeature(const nsAString &aName,
                                        ErrorResult& aRv);
 
   bool Vibrate(uint32_t aDuration);
@@ -350,11 +356,9 @@ private:
   // we'd need to figure out exactly how to trace that, and that seems to be
   // rocket science.  :(
   nsInterfaceHashtable<nsStringHashKey, nsISupports> mCachedResolveResults;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-nsresult NS_GetNavigatorUserAgent(nsAString& aUserAgent);
-
 #endif // mozilla_dom_Navigator_h
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -11440,16 +11440,17 @@ nsGlobalWindow::Observe(nsISupports* aSu
     // The user preferred languages have changed, we need to fire an event on
     // Window object and invalidate the cache for navigator.languages. It is
     // done for every change which can be a waste of cycles but those should be
     // fairly rare.
     // We MUST invalidate navigator.languages before sending the event in the
     // very likely situation where an event handler will try to read its value.
 
     if (mNavigator) {
+      NavigatorBinding::ClearCachedLanguageValue(mNavigator);
       NavigatorBinding::ClearCachedLanguagesValue(mNavigator);
     }
 
     nsCOMPtr<nsIDOMEvent> event;
     NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
     nsresult rv = event->InitEvent(NS_LITERAL_STRING("languagechange"), false, false);
     NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -74,8 +74,9 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_urlutils_stringify.html]
 [test_window_constructor.html]
 [test_window_cross_origin_props.html]
 [test_window_enumeration.html]
 [test_window_extensible.html]
 [test_window_indexing.html]
 [test_window_named_frame_enumeration.html]
 [test_writable-replaceable.html]
+[test_navigatorPrefOverride.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_navigatorPrefOverride.html
@@ -0,0 +1,54 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for navigator property override</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+  ok(navigator.appName, "This is used just to populate the cache");
+  ok(navigator.appVersion, "This is used just to populate the cache");
+
+  // B2G could have an empty platform.
+  info(navigator.platform);
+
+  ok(navigator.userAgent, "This is used just to populate the cache");
+
+  SpecialPowers.pushPrefEnv({"set": [
+    ["general.appname.override", "appName overridden"],
+    ["general.appversion.override", "appVersion overridden"],
+    ["general.platform.override", "platform overridden"],
+    ["general.useragent.override", "userAgent overridden"],
+    ]},
+    function() {
+      var ifr = document.createElement('IFRAME');
+      ifr.src = "about:blank";
+
+      ifr.addEventListener('load', function() {
+        var nav = ifr.contentWindow.navigator;
+        isnot(navigator.appName, nav.appName, "appName should match");
+        isnot(navigator.appVersion, nav.appVersion, "appVersion should match");
+        isnot(navigator.platform, nav.platform, "platform should match");
+        isnot(navigator.userAgent, nav.userAgent, "userAgent should match");
+        SimpleTest.finish();
+      }, false);
+
+      document.getElementById('content').appendChild(ifr);
+    }
+  );
+
+  SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -28,35 +28,40 @@ Navigator implements NavigatorLanguage;
 Navigator implements NavigatorOnLine;
 Navigator implements NavigatorContentUtils;
 Navigator implements NavigatorStorageUtils;
 Navigator implements NavigatorFeatures;
 
 [NoInterfaceObject]
 interface NavigatorID {
   // WebKit/Blink/Trident/Presto support this (hardcoded "Mozilla").
-  [Constant]
+  [Constant, Cached]
   readonly attribute DOMString appCodeName; // constant "Mozilla"
-  [Constant]
+  [Constant, Cached]
   readonly attribute DOMString appName;
-  [Constant]
+  [Constant, Cached]
   readonly attribute DOMString appVersion;
-  [Constant]
+  [Constant, Cached]
   readonly attribute DOMString platform;
-  [Constant]
+  [Constant, Cached]
   readonly attribute DOMString userAgent;
-  [Constant]
+  [Constant, Cached]
   readonly attribute DOMString product; // constant "Gecko"
 
   // Everyone but WebKit/Blink supports this.  See bug 679971.
   boolean taintEnabled(); // constant false
 };
 
 [NoInterfaceObject]
 interface NavigatorLanguage {
+
+  // These 2 values are cached. They are updated when pref
+  // intl.accept_languages is changed.
+
+  [Pure, Cached]
   readonly attribute DOMString? language;
   [Pure, Cached, Frozen] readonly attribute sequence<DOMString> languages;
 };
 
 [NoInterfaceObject]
 interface NavigatorOnLine {
   readonly attribute boolean onLine;
 };
--- a/dom/workers/Navigator.cpp
+++ b/dom/workers/Navigator.cpp
@@ -10,16 +10,18 @@
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseWorkerProxy.h"
 #include "mozilla/dom/WorkerNavigatorBinding.h"
 
 #include "Navigator.h"
 #include "nsProxyRelease.h"
 #include "RuntimeService.h"
 
+#include "nsIDocument.h"
+
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
 BEGIN_WORKERS_NAMESPACE
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerNavigator)
 
@@ -308,9 +310,64 @@ WorkerNavigator::GetPlatform(nsString& a
   if (!mProperties.mPlatformOverridden.IsEmpty() &&
       !workerPrivate->UsesSystemPrincipal()) {
     aPlatform = mProperties.mPlatformOverridden;
   } else {
     aPlatform = mProperties.mPlatform;
   }
 }
 
+namespace {
+
+class GetUserAgentRunnable MOZ_FINAL : public WorkerMainThreadRunnable
+{
+  nsString& mUA;
+
+public:
+  GetUserAgentRunnable(WorkerPrivate* aWorkerPrivate, nsString& aUA)
+    : WorkerMainThreadRunnable(aWorkerPrivate)
+    , mUA(aUA)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+  }
+
+  virtual bool MainThreadRun() MOZ_OVERRIDE
+  {
+    AssertIsOnMainThread();
+
+    nsCOMPtr<nsPIDOMWindow> window = mWorkerPrivate->GetWindow();
+    nsCOMPtr<nsIURI> uri;
+    if (window && window->GetDocShell()) {
+      nsIDocument* doc = window->GetExtantDoc();
+      if (doc) {
+        doc->NodePrincipal()->GetURI(getter_AddRefs(uri));
+      }
+    }
+
+    bool isCallerChrome = mWorkerPrivate->UsesSystemPrincipal();
+    nsresult rv = dom::Navigator::GetUserAgent(window, uri,
+                                               isCallerChrome, mUA);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to retrieve user-agent from the worker thread.");
+    }
+
+    return true;
+  }
+};
+
+} // anonymous namespace
+
+void
+WorkerNavigator::GetUserAgent(nsString& aUserAgent) const
+{
+  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(workerPrivate);
+
+  nsRefPtr<GetUserAgentRunnable> runnable =
+    new GetUserAgentRunnable(workerPrivate, aUserAgent);
+
+  if (!runnable->Dispatch(workerPrivate->GetJSContext())) {
+    JS_ReportPendingException(workerPrivate->GetJSContext());
+  }
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/Navigator.h
+++ b/dom/workers/Navigator.h
@@ -74,20 +74,17 @@ public:
     aProduct.AssignLiteral("Gecko");
   }
 
   bool TaintEnabled() const
   {
     return false;
   }
 
-  void GetUserAgent(nsString& aUserAgent) const
-  {
-    aUserAgent = mProperties.mUserAgent;
-  }
+  void GetUserAgent(nsString& aUserAgent) const;
 
   bool OnLine() const
   {
     return mOnline;
   }
 
   // Worker thread only!
   void SetOnLine(bool aOnline)
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1483,18 +1483,17 @@ RuntimeService::RegisterWorker(JSContext
   }
   else {
     if (!mNavigatorPropertiesLoaded) {
       Navigator::AppName(mNavigatorProperties.mAppName,
                          false /* aUsePrefOverriddenValue */);
       if (NS_FAILED(Navigator::GetAppVersion(mNavigatorProperties.mAppVersion,
                                              false /* aUsePrefOverriddenValue */)) ||
           NS_FAILED(Navigator::GetPlatform(mNavigatorProperties.mPlatform,
-                                           false /* aUsePrefOverriddenValue */)) ||
-          NS_FAILED(NS_GetNavigatorUserAgent(mNavigatorProperties.mUserAgent))) {
+                                           false /* aUsePrefOverriddenValue */))) {
         JS_ReportError(aCx, "Failed to load navigator strings!");
         UnregisterWorker(aCx, aWorkerPrivate);
         return false;
       }
 
       // The navigator overridden properties should have already been read.
 
       mNavigatorPropertiesLoaded = true;
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -105,17 +105,16 @@ public:
   struct NavigatorProperties
   {
     nsString mAppName;
     nsString mAppNameOverridden;
     nsString mAppVersion;
     nsString mAppVersionOverridden;
     nsString mPlatform;
     nsString mPlatformOverridden;
-    nsString mUserAgent;
   };
 
 private:
   NavigatorProperties mNavigatorProperties;
 
   // True when the observer service holds a reference to this object.
   bool mObserved;
   bool mShuttingDown;
--- a/dom/workers/test/test_bug1062920.html
+++ b/dom/workers/test/test_bug1062920.html
@@ -14,31 +14,40 @@
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
 <script class="testbody" type="text/javascript">
 
   function checkValues() {
     var worker = new Worker("bug1062920_worker.js");
 
     worker.onmessage = function(event) {
-      is(event.data.appCodeName, navigator.appCodeName, "appCodeName should match");
-      is(event.data.appName, navigator.appName, "appName should match");
-      is(event.data.appVersion, navigator.appVersion, "appVersion should match");
-      is(event.data.platform, navigator.platform, "platform should match");
-      is(event.data.userAgent, navigator.userAgent, "userAgent should match");
-      is(event.data.product, navigator.product, "product should match");
-      runTests();
+      var ifr = document.createElement('IFRAME');
+      ifr.src = "about:blank";
+
+      ifr.addEventListener('load', function() {
+        var nav = ifr.contentWindow.navigator;
+        is(event.data.appCodeName, nav.appCodeName, "appCodeName should match");
+        is(event.data.appName, nav.appName, "appName should match");
+        is(event.data.appVersion, nav.appVersion, "appVersion should match");
+        is(event.data.platform, nav.platform, "platform should match");
+        is(event.data.userAgent, nav.userAgent, "userAgent should match");
+        is(event.data.product, nav.product, "product should match");
+        runTests();
+      }, false);
+
+      document.getElementById('content').appendChild(ifr);
     };
   }
 
   function replaceAndCheckValues() {
     SpecialPowers.pushPrefEnv({"set": [
       ["general.appname.override", "appName overridden"],
       ["general.appversion.override", "appVersion overridden"],
-      ["general.platform.override", "platform overridden"]
+      ["general.platform.override", "platform overridden"],
+      ["general.useragent.override", "userAgent overridden"]
     ]}, checkValues);
   }
 
   var tests = [
     checkValues,
     replaceAndCheckValues
   ];
 
--- a/dom/workers/test/test_bug1062920.xul
+++ b/dom/workers/test/test_bug1062920.xul
@@ -30,23 +30,24 @@
       runTests();
     };
   }
 
   function replaceAndCheckValues() {
     SpecialPowers.pushPrefEnv({"set": [
       ["general.appname.override", "appName overridden"],
       ["general.appversion.override", "appVersion overridden"],
-      ["general.platform.override", "platform overridden"]
+      ["general.platform.override", "platform overridden"],
+      ["general.useragent.override", "userAgent overridden"]
     ]}, checkValues);
   }
 
   var tests = [
-    checkValues,
-    replaceAndCheckValues
+    replaceAndCheckValues,
+    checkValues
   ];
 
   function runTests() {
     if (tests.length == 0) {
       SimpleTest.finish();
       return;
     }
 
--- a/netwerk/test/mochitests/test_user_agent_overrides.html
+++ b/netwerk/test/mochitests/test_user_agent_overrides.html
@@ -63,43 +63,63 @@ function testUA(options, callback) {
   }
   test_hosts.forEach(function (test_host) {
     test_ua.push(getUA(test_host));
   });
   // set the override pref to override the UA
   SpecialPowers.pushPrefEnv({
     set: [[PREF_OVERRIDES_BRANCH + domain, override]],
   }, function () {
-    // check that the UA has changed after pref change
-    if (overrideNavigator) {
-      is(navigator.userAgent, expected,
-        'Navigator UA not overridden at step ' + (++step));
-    } else {
-      is(navigator.userAgent, DEFAULT_UA,
-        'Navigator UA should not be overridden at step ' + (++step));
-    }
-    test_hosts.forEach(function (test_host) {
-      is(getUA(test_host), expected,
-        'Header UA not overridden at step ' + (++step));
-    });
-    // clear the override pref to undo overriding the UA
-    SpecialPowers.pushPrefEnv({
-      clear: [[PREF_OVERRIDES_BRANCH + domain]],
-    }, function () {
-      // check that the UA has changed back
+    var ifr = document.createElement('IFRAME');
+    ifr.src = "about:blank";
+
+    ifr.addEventListener('load', function() {
+      var nav = ifr.contentWindow.navigator;
+
+      // check that the UA has changed after pref change
       if (overrideNavigator) {
-        is(navigator.userAgent, navigator_ua,
-          'Navigator UA not restored at step ' + (++step));
+        is(nav.userAgent, expected,
+          'Navigator UA not overridden at step ' + (++step));
+      } else {
+        is(nav.userAgent, DEFAULT_UA,
+          'Navigator UA should not be overridden at step ' + (++step));
       }
-      test_hosts.forEach(function (test_host, i) {
-        is(getUA(test_host), test_ua[i],
-          'Header UA not restored at step ' + (++step));
+
+      test_hosts.forEach(function (test_host) {
+        is(getUA(test_host), expected,
+          'Header UA not overridden at step ' + (++step));
       });
-      callback();
-    });
+
+      // clear the override pref to undo overriding the UA
+      SpecialPowers.pushPrefEnv({
+        clear: [[PREF_OVERRIDES_BRANCH + domain]],
+      }, function () {
+        var ifr = document.createElement('IFRAME');
+        ifr.src = "about:blank";
+
+        ifr.addEventListener('load', function() {
+          var nav = ifr.contentWindow.navigator;
+
+          // check that the UA has changed back
+          if (overrideNavigator) {
+            is(nav.userAgent, navigator_ua,
+              'Navigator UA not restored at step ' + (++step));
+          }
+          test_hosts.forEach(function (test_host, i) {
+            is(getUA(test_host), test_ua[i],
+              'Header UA not restored at step ' + (++step));
+          });
+          callback();
+        });
+
+        document.getElementById('content').appendChild(ifr);
+      });
+    }, false);
+
+    document.getElementById('content').appendChild(ifr);
   });
 }
 
 
 var step = 0; // for logging
 var tests = [
   // should override both header and navigator.userAgent
   {
--- a/netwerk/test/mochitests/test_user_agent_updates.html
+++ b/netwerk/test/mochitests/test_user_agent_updates.html
@@ -116,50 +116,66 @@ function testDownload(callback) {
   SpecialPowers.pushPrefEnv({
     set: [
       [PREF_UPDATES_ENABLED, true],
       [PREF_UPDATES_URL, url],
       [PREF_UPDATES_TIMEOUT, 10000],
       [PREF_UPDATES_INTERVAL, 1] // 1 second interval
     ]
   }, function waitForUpdate() setTimeout(function () {
-    if (navigator.userAgent !== UA_OVERRIDE) {
-      waitForUpdate();
-      return;
-    }
-    info('Overrode navigator UA');
-    is(getUA(location.origin), UA_OVERRIDE, 'Header UA not overridden');
+    var ifr = document.createElement('IFRAME');
+    ifr.src = "about:blank";
+
+    ifr.addEventListener('load', function() {
+      var nav = ifr.contentWindow.navigator;
+      if (nav.userAgent !== UA_OVERRIDE) {
+        waitForUpdate();
+        return;
+      }
+
+      info('Overrode navigator UA');
+      is(getUA(location.origin), UA_OVERRIDE, 'Header UA not overridden');
 
-    var updateTime = parseInt(getUA('http://example.org'));
-    ok(startTime <= updateTime, 'Update was before start time');
-    ok(updateTime <= Date.now(), 'Update was after present time');
+      var updateTime = parseInt(getUA('http://example.org'));
+      ok(startTime <= updateTime, 'Update was before start time');
+      ok(updateTime <= Date.now(), 'Update was after present time');
 
-    OVERRIDES.forEach(function (val) {
-      val.expected && is(getUA(val.host), val.expected,
-        'Incorrect URL parameter: ' + val.override);
-    });
-    callback();
+      OVERRIDES.forEach(function (val) {
+        val.expected && is(getUA(val.host), val.expected,
+          'Incorrect URL parameter: ' + val.override);
+      });
+      callback();
+    }, false);
+
+    document.getElementById('content').appendChild(ifr);
   }, 100));
 }
 
 function testBadUpdate(callback) {
   var url = getServerURL() + 'invalid-json';
   var prevOverride = navigator.userAgent;
   SpecialPowers.pushPrefEnv({
     set: [
       [PREF_UPDATES_URL, url],
       [PREF_UPDATES_INTERVAL, 1] // 1 second interval
     ]
   }, function () setTimeout(function () {
-    // We want to make sure a bad update doesn't cancel out previous overrides.
-    // We do this by waiting for 5 seconds (assuming the update occurs within 5
-    // seconds), and check that the previous override hasn't changed.
-    is(navigator.userAgent, prevOverride,
-      'Invalid update deleted previous override');
-    callback();
+    var ifr = document.createElement('IFRAME');
+    ifr.src = "about:blank";
+
+    ifr.addEventListener('load', function() {
+      // We want to make sure a bad update doesn't cancel out previous
+      // overrides. We do this by waiting for 5 seconds (assuming the update
+      // occurs within 5 seconds), and check that the previous override hasn't
+      // changed.
+      is(navigator.userAgent, prevOverride,
+        'Invalid update deleted previous override');
+      callback();
+    }, false);
+    document.getElementById('content').appendChild(ifr);
   }, 5000));
 }
 
 function testProfileLoad(callback) {
   var file = FU.getFile(KEY_APPDIR, [FILE_UPDATES]).path;
   var encoder = SpecialPowers.wrap(new TextEncoder());
   var overrides = {};
   overrides[location.hostname] = UA_ALT_OVERRIDE;
@@ -174,23 +190,31 @@ function testProfileLoad(callback) {
     () => {
       SpecialPowers.pushPrefEnv({
         set: [[PREF_UPDATES_ENABLED, true]]
       }, function () {
         // initialize UserAgentOverrides.jsm and
         // UserAgentUpdates.jsm and load saved file
         UAO.init();
         (function waitForLoad() {
-          if (navigator.userAgent !== UA_ALT_OVERRIDE) {
-            setTimeout(waitForLoad, 100);
-            return;
-          }
-          is(getUA(location.origin), UA_ALT_OVERRIDE, 'Did not apply saved override');
-          saveFilePreviousSize = file.fileSize;
-          callback();
+          var ifr = document.createElement('IFRAME');
+          ifr.src = "about:blank";
+
+          ifr.addEventListener('load', function() {
+            var nav = ifr.contentWindow.navigator;
+            if (nav.userAgent !== UA_ALT_OVERRIDE) {
+              setTimeout(waitForLoad, 100);
+              return;
+            }
+            is(getUA(location.origin), UA_ALT_OVERRIDE, 'Did not apply saved override');
+            saveFilePreviousSize = file.fileSize;
+            callback();
+          }, false);
+
+          document.getElementById('content').appendChild(ifr);
         })();
       });
     },
     (reason) => {
       throw reason
     }
   );
 }
@@ -264,9 +288,8 @@ SpecialPowers.pushPrefEnv({
     )
   );
 });
 
 </script>
 </pre>
 </body>
 </html>
-