Bug 1275833 - Add window.customElements and CustomElementsRegistry interface for custom element v1. r=smaug, wchen
authorJohn Dai <jdai@mozilla.com>
Sun, 24 Jul 2016 23:38:00 +0200
changeset 331618 341f218569ba65c5f4ce73ef5ee6e76480b78526
parent 331617 970ad562913ff0ac4a6027411f3ebf7ddd0b8427
child 331619 13a2ea0523a8ba7cf2e5067093b280f356ae572b
push id9858
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 14:37:10 +0000
treeherdermozilla-aurora@203106ef6cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, wchen
bugs1275833
milestone50.0a1
Bug 1275833 - Add window.customElements and CustomElementsRegistry interface for custom element v1. r=smaug, wchen
dom/base/CustomElementsRegistry.cpp
dom/base/CustomElementsRegistry.h
dom/base/moz.build
dom/base/nsDocument.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsPIDOMWindow.h
dom/webidl/CustomElementsRegistry.webidl
dom/webidl/Document.webidl
dom/webidl/Window.webidl
dom/webidl/moz.build
modules/libpref/init/all.js
testing/profiles/prefs_general.js
new file mode 100644
--- /dev/null
+++ b/dom/base/CustomElementsRegistry.cpp
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/CustomElementsRegistry.h"
+#include "mozilla/dom/CustomElementsRegistryBinding.h"
+#include "mozilla/dom/WebComponentsBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+
+// Only needed for refcounted objects.
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CustomElementsRegistry, mWindow)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(CustomElementsRegistry)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(CustomElementsRegistry)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CustomElementsRegistry)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+/* static */ bool
+CustomElementsRegistry::IsCustomElementsEnabled(JSContext* aCx, JSObject* aObject)
+{
+  JS::Rooted<JSObject*> obj(aCx, aObject);
+  if (Preferences::GetBool("dom.webcomponents.customelements.enabled") ||
+      nsDocument::IsWebComponentsEnabled(aCx, obj)) {
+    return true;
+  }
+
+  return false;
+}
+
+/* static */ already_AddRefed<CustomElementsRegistry>
+CustomElementsRegistry::Create(nsPIDOMWindowInner* aWindow)
+{
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsInnerWindow());
+
+  if (!aWindow->GetDocShell()) {
+    return nullptr;
+  }
+
+  RefPtr<CustomElementsRegistry> customElementsRegistry =
+    new CustomElementsRegistry(aWindow);
+  return customElementsRegistry.forget();
+}
+
+CustomElementsRegistry::CustomElementsRegistry(nsPIDOMWindowInner* aWindow)
+ : mWindow(aWindow)
+{
+}
+
+CustomElementsRegistry::~CustomElementsRegistry()
+{
+}
+
+JSObject*
+CustomElementsRegistry::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return CustomElementsRegistryBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsISupports* CustomElementsRegistry::GetParentObject() const
+{
+  return mWindow;
+}
+
+void CustomElementsRegistry::Define(const nsAString& aName,
+                                    Function& aFunctionConstructor,
+                                    const ElementDefinitionOptions& aOptions,
+                                    ErrorResult& aRv)
+{
+  // TODO: This function will be implemented in bug 1275835
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+}
+
+void
+CustomElementsRegistry::Get(JSContext* aCx, const nsAString& aName,
+                            JS::MutableHandle<JS::Value> aRetVal,
+                            ErrorResult& aRv)
+{
+  // TODO: This function will be implemented in bug 1275838
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+}
+
+already_AddRefed<Promise>
+CustomElementsRegistry::WhenDefined(const nsAString& name, ErrorResult& aRv)
+{
+  // TODO: This function will be implemented in bug 1275839
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  return nullptr;
+}
+
+} // namespace dom
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/CustomElementsRegistry.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_CustomElementsRegistry_h
+#define mozilla_dom_CustomElementsRegistry_h
+
+#include "js/TypeDecls.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "mozilla/dom/FunctionBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+struct ElementDefinitionOptions;
+struct LifecycleCallbacks;
+class Function;
+class Promise;
+
+class CustomElementsRegistry final : public nsISupports,
+                                     public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CustomElementsRegistry)
+
+public:
+  static bool IsCustomElementsEnabled(JSContext* aCx, JSObject* aObject);
+  static already_AddRefed<CustomElementsRegistry> Create(nsPIDOMWindowInner* aWindow);
+  already_AddRefed<nsIDocument> GetOwnerDocument() const;
+
+private:
+  explicit CustomElementsRegistry(nsPIDOMWindowInner* aWindow);
+  ~CustomElementsRegistry();
+  nsCOMPtr<nsPIDOMWindowInner> mWindow;
+
+public:
+  nsISupports* GetParentObject() const;
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  void Define(const nsAString& aName, Function& aFunctionConstructor,
+              const ElementDefinitionOptions& aOptions, ErrorResult& aRv);
+
+  void Get(JSContext* cx, const nsAString& name,
+           JS::MutableHandle<JS::Value> aRetVal, ErrorResult& aRv);
+
+  already_AddRefed<Promise> WhenDefined(const nsAString& name, ErrorResult& aRv);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+
+#endif // mozilla_dom_CustomElementsRegistry_h
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -151,16 +151,17 @@ EXPORTS.mozilla.dom += [
     'Attr.h',
     'BarProps.h',
     'BlobSet.h',
     'BodyUtil.h',
     'ChildIterator.h',
     'ChromeNodeList.h',
     'ChromeUtils.h',
     'Comment.h',
+    'CustomElementsRegistry.h',
     'DirectionalityUtils.h',
     'DocumentFragment.h',
     'DocumentType.h',
     'DOMCursor.h',
     'DOMError.h',
     'DOMException.h',
     'DOMImplementation.h',
     'DOMMatrix.h',
@@ -211,16 +212,17 @@ UNIFIED_SOURCES += [
     'BarProps.cpp',
     'BlobSet.cpp',
     'BodyUtil.cpp',
     'ChildIterator.cpp',
     'ChromeNodeList.cpp',
     'ChromeUtils.cpp',
     'Comment.cpp',
     'Crypto.cpp',
+    'CustomElementsRegistry.cpp',
     'DirectionalityUtils.cpp',
     'DocumentFragment.cpp',
     'DocumentType.cpp',
     'DOMCursor.cpp',
     'DOMError.cpp',
     'DOMException.cpp',
     'DOMImplementation.cpp',
     'DOMMatrix.cpp',
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -4763,17 +4763,18 @@ nsDocument::SetScriptGlobalObject(nsIScr
 #endif
         bool allowDNSPrefetch;
         docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
         mAllowDNSPrefetch = allowDNSPrefetch;
       }
     }
 
     MaybeRescheduleAnimationFrameNotifications();
-    if (Preferences::GetBool("dom.webcomponents.enabled")) {
+    if (Preferences::GetBool("dom.webcomponents.enabled") ||
+        Preferences::GetBool("dom.webcomponents.customelements.enabled")) {
       mRegistry = new Registry();
     }
   }
 
   // Remember the pointer to our window (or lack there of), to avoid
   // having to QI every time it's asked for.
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mScriptGlobalObject);
   mWindow = window;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1560,16 +1560,17 @@ nsGlobalWindow::CleanUp()
   mMenubar = nullptr;
   mToolbar = nullptr;
   mLocationbar = nullptr;
   mPersonalbar = nullptr;
   mStatusbar = nullptr;
   mScrollbars = nullptr;
   mLocation = nullptr;
   mHistory = nullptr;
+  mCustomElements = nullptr;
   mFrames = nullptr;
   mWindowUtils = nullptr;
   mApplicationCache = nullptr;
   mIndexedDB = nullptr;
 
   mConsole = nullptr;
 
   mExternal = nullptr;
@@ -1690,16 +1691,17 @@ nsGlobalWindow::FreeInnerObjects()
 
   if (mListenerManager) {
     mListenerManager->Disconnect();
     mListenerManager = nullptr;
   }
 
   mLocation = nullptr;
   mHistory = nullptr;
+  mCustomElements = nullptr;
 
   if (mNavigator) {
     mNavigator->OnNavigation();
     mNavigator->Invalidate();
     mNavigator = nullptr;
   }
 
   if (mScreen) {
@@ -1872,16 +1874,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   for (nsTimeout* timeout = tmp->mTimeouts.getFirst();
        timeout;
        timeout = timeout->getNext()) {
     cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(nsTimeout));
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
@@ -1945,16 +1948,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   }
 
   if (tmp->mListenerManager) {
     tmp->mListenerManager->Disconnect();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomElements)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage)
   if (tmp->mApplicationCache) {
     static_cast<nsDOMOfflineResourceList*>(tmp->mApplicationCache.get())->Disconnect();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache)
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
@@ -3827,16 +3831,27 @@ nsGlobalWindow::GetHistory(ErrorResult& 
 
   if (!mHistory) {
     mHistory = new nsHistory(AsInner());
   }
 
   return mHistory;
 }
 
+CustomElementsRegistry*
+nsGlobalWindow::CustomElements()
+{
+  MOZ_RELEASE_ASSERT(IsInnerWindow());
+  if (!mCustomElements) {
+      mCustomElements = CustomElementsRegistry::Create(AsInner());
+  }
+
+  return mCustomElements;
+}
+
 Performance*
 nsPIDOMWindowInner::GetPerformance()
 {
   MOZ_ASSERT(IsInnerWindow());
   CreatePerformanceObjectIfNeeded();
   return mPerformance;
 }
 
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -101,16 +101,17 @@ class nsWindowSizes;
 
 namespace mozilla {
 class DOMEventTargetHelper;
 namespace dom {
 class BarProp;
 struct ChannelPixelLayout;
 class Console;
 class Crypto;
+class CustomElementsRegistry;
 class External;
 class Function;
 class Gamepad;
 enum class ImageBitmapFormat : uint32_t;
 class MediaQueryList;
 class MozSelfSupport;
 class Navigator;
 class OwningExternalOrWindowProxy;
@@ -872,16 +873,17 @@ public:
   }
   void GetNameOuter(nsAString& aName);
   void GetName(nsAString& aName, mozilla::ErrorResult& aError);
   void SetNameOuter(const nsAString& aName, mozilla::ErrorResult& aError);
   void SetName(const nsAString& aName, mozilla::ErrorResult& aError);
   nsLocation* GetLocation(mozilla::ErrorResult& aError);
   nsIDOMLocation* GetLocation() override;
   nsHistory* GetHistory(mozilla::ErrorResult& aError);
+  mozilla::dom::CustomElementsRegistry* CustomElements() override;
   mozilla::dom::BarProp* GetLocationbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetMenubar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetPersonalbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetScrollbars(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetStatusbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetToolbar(mozilla::ErrorResult& aError);
   void GetStatusOuter(nsAString& aStatus);
   void GetStatus(nsAString& aStatus, mozilla::ErrorResult& aError);
@@ -1832,16 +1834,17 @@ protected:
   // If mTimeoutInsertionPoint is non-null, insertions should happen after it.
   // This is a dummy timeout at the moment; if that ever changes, the logic in
   // ResetTimersForNonBackgroundWindow needs to change.
   nsTimeout*                    mTimeoutInsertionPoint;
   uint32_t                      mTimeoutPublicIdCounter;
   uint32_t                      mTimeoutFiringDepth;
   RefPtr<nsLocation>          mLocation;
   RefPtr<nsHistory>           mHistory;
+  RefPtr<mozilla::dom::CustomElementsRegistry> mCustomElements;
 
   // These member variables are used on both inner and the outer windows.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
 
   typedef nsTArray<RefPtr<mozilla::dom::StorageEvent>> nsDOMStorageEventArray;
   nsDOMStorageEventArray mPendingStorageEvents;
 
   uint32_t mTimeoutsSuspendDepth;
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -39,16 +39,17 @@ struct nsTimeout;
 typedef uint32_t SuspendTypes;
 
 namespace mozilla {
 namespace dom {
 class AudioContext;
 class Element;
 class Performance;
 class ServiceWorkerRegistration;
+class CustomElementsRegistry;
 } // namespace dom
 namespace gfx {
 class VRDeviceProxy;
 } // namespace gfx
 } // namespace mozilla
 
 // Popup control state enum. The values in this enum must go from most
 // permissive to least permissive so that it's safe to push state in
@@ -91,17 +92,17 @@ class nsPIDOMWindow : public T
 {
 public:
   nsPIDOMWindowInner* AsInner();
   const nsPIDOMWindowInner* AsInner() const;
   nsPIDOMWindowOuter* AsOuter();
   const nsPIDOMWindowOuter* AsOuter() const;
 
   virtual nsPIDOMWindowOuter* GetPrivateRoot() = 0;
-
+  virtual mozilla::dom::CustomElementsRegistry* CustomElements() = 0;
   // Outer windows only.
   virtual void ActivateOrDeactivate(bool aActivate) = 0;
 
   // this is called GetTopWindowRoot to avoid conflicts with nsIDOMWindow::GetWindowRoot
   /**
    * |top| gets the root of the window hierarchy.
    *
    * This function does not cross chrome-content boundaries, so if this
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CustomElementsRegistry.webidl
@@ -0,0 +1,20 @@
+/* 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/. */
+
+// https://html.spec.whatwg.org/#dom-window-customelements
+[Func="CustomElementsRegistry::IsCustomElementsEnabled"]
+interface CustomElementsRegistry {
+  [Throws]
+  void define(DOMString name, Function functionConstructor,
+              optional ElementDefinitionOptions options);
+  [Throws]
+  any get(DOMString name);
+  [Throws]
+  Promise<void> whenDefined(DOMString name);
+};
+
+dictionary ElementDefinitionOptions {
+  DOMString extends;
+};
+
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -256,17 +256,18 @@ partial interface Document {
 // http://dvcs.w3.org/hg/pointerlock/raw-file/default/index.html#extensions-to-the-document-interface
 partial interface Document {
     readonly attribute Element? mozPointerLockElement;
     void mozExitPointerLock ();
 };
 
 //http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
 partial interface Document {
-    [Throws, Func="nsDocument::IsWebComponentsEnabled"]
+    // this is deprecated from CustomElements v0
+    [Throws, Func="CustomElementsRegistry::IsCustomElementsEnabled"]
     object registerElement(DOMString name, optional ElementRegistrationOptions options);
 };
 
 // http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html#sec-document-interface
 partial interface Document {
   readonly attribute boolean hidden;
   readonly attribute boolean mozHidden;
   readonly attribute VisibilityState visibilityState;
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -31,16 +31,18 @@ typedef any Transferable;
    CrossOriginReadable] readonly attribute Window window;
   [Replaceable, Constant, StoreInSlot,
    CrossOriginReadable] readonly attribute Window self;
   [Unforgeable, StoreInSlot, Pure] readonly attribute Document? document;
   [Throws] attribute DOMString name;
   [PutForwards=href, Unforgeable, Throws,
    CrossOriginReadable, CrossOriginWritable] readonly attribute Location? location;
   [Throws] readonly attribute History history;
+  [Func="CustomElementsRegistry::IsCustomElementsEnabled"]
+  readonly attribute CustomElementsRegistry customElements;
   [Replaceable, Throws] readonly attribute BarProp locationbar;
   [Replaceable, Throws] readonly attribute BarProp menubar;
   [Replaceable, Throws] readonly attribute BarProp personalbar;
   [Replaceable, Throws] readonly attribute BarProp scrollbars;
   [Replaceable, Throws] readonly attribute BarProp statusbar;
   [Replaceable, Throws] readonly attribute BarProp toolbar;
   [Throws] attribute DOMString status;
   [Throws, CrossOriginCallable, UnsafeInPrerendering] void close();
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -102,16 +102,17 @@ WEBIDL_FILES = [
     'CSSPrimitiveValue.webidl',
     'CSSPseudoElement.webidl',
     'CSSRuleList.webidl',
     'CSSStyleDeclaration.webidl',
     'CSSStyleSheet.webidl',
     'CSSTransition.webidl',
     'CSSValue.webidl',
     'CSSValueList.webidl',
+    'CustomElementsRegistry.webidl',
     'DataContainerEvent.webidl',
     'DataTransfer.webidl',
     'DataTransferItem.webidl',
     'DataTransferItemList.webidl',
     'DecoderDoctorNotification.webidl',
     'DedicatedWorkerGlobalScope.webidl',
     'DelayNode.webidl',
     'DesktopNotification.webidl',
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1174,16 +1174,17 @@ pref("dom.event.contextmenu.enabled",   
 pref("dom.event.clipboardevents.enabled",   true);
 #if defined(XP_WIN) && !defined(RELEASE_BUILD) || defined(MOZ_WIDGET_GTK) && !defined(RELEASE_BUILD)
 pref("dom.event.highrestimestamp.enabled",  true);
 #else
 pref("dom.event.highrestimestamp.enabled",  false);
 #endif
 
 pref("dom.webcomponents.enabled",           false);
+pref("dom.webcomponents.customelements.enabled", false);
 
 pref("javascript.enabled",                  true);
 pref("javascript.options.strict",           false);
 #ifdef DEBUG
 pref("javascript.options.strict.debug",     false);
 #endif
 pref("javascript.options.baselinejit",      true);
 pref("javascript.options.ion",              true);
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -45,16 +45,17 @@ user_pref("security.warn_viewing_mixed",
 user_pref("app.update.enabled", false);
 user_pref("app.update.staging.enabled", false);
 user_pref("app.update.url.android", "");
 // Make sure GMPInstallManager won't hit the network.
 user_pref("media.gmp-manager.url.override", "http://%(server)s/dummy-gmp-manager.xml");
 user_pref("dom.w3c_touch_events.enabled", 1);
 user_pref("dom.undo_manager.enabled", true);
 user_pref("dom.webcomponents.enabled", true);
+user_pref("dom.webcomponents.customelements.enabled", true);
 user_pref("dom.htmlimports.enabled", true);
 // Existing tests assume there is no font size inflation.
 user_pref("font.size.inflation.emPerLine", 0);
 user_pref("font.size.inflation.minTwips", 0);
 
 // AddonManager tests require that the experiments provider be present.
 user_pref("experiments.supported", true);
 // Point the manifest at something local so we don't risk it hitting production