Merge mozilla-central and inbound
authorEd Morley <emorley@mozilla.com>
Fri, 02 Aug 2013 13:50:46 +0100
changeset 153413 f06052c0986b0a9b392ede7d9a067e65653d7d71
parent 153396 d3a0ac52d5d44452afc354567dc4ed527dd9d393 (current diff)
parent 153412 c116372d7ad481f81b44dcd3c364d0e220dac6b0 (diff)
child 153414 8e1fc89b12ca799ff6483c81e36f118db6fbebba
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone25.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 mozilla-central and inbound
--- a/content/html/content/src/HTMLInputElement.cpp
+++ b/content/html/content/src/HTMLInputElement.cpp
@@ -471,143 +471,122 @@ nsColorPickerShownCallback::Done(const n
                                               false);
   }
 
   return rv;
 }
 
 NS_IMPL_ISUPPORTS1(nsColorPickerShownCallback, nsIColorPickerShownCallback)
 
-HTMLInputElement::AsyncClickHandler::AsyncClickHandler(HTMLInputElement* aInput)
-  : mInput(aInput)
-{
-  nsPIDOMWindow* win = aInput->OwnerDoc()->GetWindow();
-  if (win) {
-    mPopupControlState = win->GetPopupControlState();
-  }
-}
-
-NS_IMETHODIMP
-HTMLInputElement::AsyncClickHandler::Run()
-{
-  if (mInput->GetType() == NS_FORM_INPUT_FILE) {
-    return InitFilePicker();
-  } else if (mInput->GetType() == NS_FORM_INPUT_COLOR) {
-    return InitColorPicker();
-  }
-  return NS_ERROR_FAILURE;
+bool
+HTMLInputElement::IsPopupBlocked() const
+{
+  nsCOMPtr<nsPIDOMWindow> win = OwnerDoc()->GetWindow();
+  MOZ_ASSERT(win, "window should not be null");
+  if (!win) {
+    return true;
+  }
+
+  // Check if page is allowed to open the popup
+  if (win->GetPopupControlState() <= openControlled) {
+    return false;
+  }
+
+  nsCOMPtr<nsIPopupWindowManager> pm = do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
+  if (!pm) {
+    return true;
+  }
+
+  uint32_t permission;
+  pm->TestPermission(OwnerDoc()->NodePrincipal(), &permission);
+  return permission == nsIPopupWindowManager::DENY_POPUP;
 }
 
 nsresult
-HTMLInputElement::AsyncClickHandler::InitColorPicker()
-{
-  // Get parent nsPIDOMWindow object.
-  nsCOMPtr<nsIDocument> doc = mInput->OwnerDoc();
+HTMLInputElement::InitColorPicker()
+{
+  nsCOMPtr<nsIDocument> doc = OwnerDoc();
 
   nsCOMPtr<nsPIDOMWindow> win = doc->GetWindow();
   if (!win) {
     return NS_ERROR_FAILURE;
   }
 
-  // Check if page is allowed to open the popup
-  if (mPopupControlState > openControlled) {
-    nsCOMPtr<nsIPopupWindowManager> pm =
-      do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
-
-    if (!pm) {
-      return NS_OK;
-    }
-
-    uint32_t permission;
-    pm->TestPermission(doc->NodePrincipal(), &permission);
-    if (permission == nsIPopupWindowManager::DENY_POPUP) {
-      nsGlobalWindow::FirePopupBlockedEvent(doc, win, nullptr, EmptyString(), EmptyString());
-      return NS_OK;
-    }
+  if (IsPopupBlocked()) {
+    nsGlobalWindow::FirePopupBlockedEvent(doc, win, nullptr, EmptyString(), EmptyString());
+    return NS_OK;
   }
 
   // Get Loc title
   nsXPIDLString title;
   nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                      "ColorPicker", title);
 
   nsCOMPtr<nsIColorPicker> colorPicker = do_CreateInstance("@mozilla.org/colorpicker;1");
   if (!colorPicker) {
     return NS_ERROR_FAILURE;
   }
 
   nsAutoString initialValue;
-  mInput->GetValueInternal(initialValue);
+  GetValueInternal(initialValue);
   nsresult rv = colorPicker->Init(win, title, initialValue);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIColorPickerShownCallback> callback =
-    new nsColorPickerShownCallback(mInput, colorPicker);
+    new nsColorPickerShownCallback(this, colorPicker);
 
   return colorPicker->Open(callback);
 }
 
 nsresult
-HTMLInputElement::AsyncClickHandler::InitFilePicker()
+HTMLInputElement::InitFilePicker()
 {
   // Get parent nsPIDOMWindow object.
-  nsCOMPtr<nsIDocument> doc = mInput->OwnerDoc();
-
-  nsPIDOMWindow* win = doc->GetWindow();
+  nsCOMPtr<nsIDocument> doc = OwnerDoc();
+
+  nsCOMPtr<nsPIDOMWindow> win = doc->GetWindow();
   if (!win) {
     return NS_ERROR_FAILURE;
   }
 
-  // Check if page is allowed to open the popup
-  if (mPopupControlState > openControlled) {
-    nsCOMPtr<nsIPopupWindowManager> pm =
-      do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
-
-    if (!pm) {
-      return NS_OK;
-    }
-
-    uint32_t permission;
-    pm->TestPermission(doc->NodePrincipal(), &permission);
-    if (permission == nsIPopupWindowManager::DENY_POPUP) {
-      nsGlobalWindow::FirePopupBlockedEvent(doc, win, nullptr, EmptyString(), EmptyString());
-      return NS_OK;
-    }
+  if (IsPopupBlocked()) {
+    nsGlobalWindow::FirePopupBlockedEvent(doc, win, nullptr, EmptyString(), EmptyString());
+    return NS_OK;
   }
 
   // Get Loc title
   nsXPIDLString title;
   nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                      "FileUpload", title);
 
   nsCOMPtr<nsIFilePicker> filePicker = do_CreateInstance("@mozilla.org/filepicker;1");
   if (!filePicker)
     return NS_ERROR_FAILURE;
 
-  bool multi = mInput->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple);
+  bool multi = HasAttr(kNameSpaceID_None, nsGkAtoms::multiple);
 
   nsresult rv = filePicker->Init(win, title,
                                  multi
                                   ? static_cast<int16_t>(nsIFilePicker::modeOpenMultiple)
                                   : static_cast<int16_t>(nsIFilePicker::modeOpen));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (mInput->HasAttr(kNameSpaceID_None, nsGkAtoms::accept)) {
-    mInput->SetFilePickerFiltersFromAccept(filePicker);
+  if (HasAttr(kNameSpaceID_None, nsGkAtoms::accept)) {
+    SetFilePickerFiltersFromAccept(filePicker);
   } else {
     filePicker->AppendFilters(nsIFilePicker::filterAll);
   }
 
   // Set default directry and filename
   nsAutoString defaultName;
 
-  const nsCOMArray<nsIDOMFile>& oldFiles = mInput->GetFilesInternal();
+  const nsCOMArray<nsIDOMFile>& oldFiles = GetFilesInternal();
 
   nsCOMPtr<nsIFilePickerShownCallback> callback =
-    new HTMLInputElement::nsFilePickerShownCallback(mInput, filePicker, multi);
+    new HTMLInputElement::nsFilePickerShownCallback(this, filePicker, multi);
 
   if (oldFiles.Count()) {
     nsString path;
 
     oldFiles[0]->GetMozFullPathInternal(path);
 
     nsCOMPtr<nsIFile> localFile;
     rv = NS_NewLocalFile(path, false, getter_AddRefs(localFile));
@@ -2591,23 +2570,16 @@ HTMLInputElement::SelectAll(nsPresContex
 {
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
 
   if (formControlFrame) {
     formControlFrame->SetFormProperty(nsGkAtoms::select, EmptyString());
   }
 }
 
-NS_IMETHODIMP
-HTMLInputElement::FireAsyncClickHandler()
-{
-  nsCOMPtr<nsIRunnable> event = new AsyncClickHandler(this);
-  return NS_DispatchToMainThread(event);
-}
-
 bool
 HTMLInputElement::NeedToInitializeEditorForEvent(nsEventChainPreVisitor& aVisitor) const
 {
   // We only need to initialize the editor for single line input controls because they
   // are lazily initialized.  We don't need to initialize the control for
   // certain types of events, because we know that those events are safe to be
   // handled without the editor being initialized.  These events include:
   // mousein/move/out, and DOM mutation events.
@@ -2901,41 +2873,45 @@ HTMLInputElement::ShouldPreventDOMActiva
   }
 
   return target->GetParent() == this &&
          target->IsRootOfNativeAnonymousSubtree() &&
          target->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                              nsGkAtoms::button, eCaseMatters);
 }
 
-void
-HTMLInputElement::MaybeFireAsyncClickHandler(nsEventChainPostVisitor& aVisitor)
+nsresult
+HTMLInputElement::MaybeInitPickers(nsEventChainPostVisitor& aVisitor)
 {
   // Open a file picker when we receive a click on a <input type='file'>, or
   // open a color picker when we receive a click on a <input type='color'>.
   // A click is handled in the following cases:
   // - preventDefault() has not been called (or something similar);
   // - it's the left mouse button.
   // We do not prevent non-trusted click because authors can already use
-  // .click(). However, the file picker will follow the rules of popup-blocking.
-  if ((mType == NS_FORM_INPUT_FILE || mType == NS_FORM_INPUT_COLOR) &&
-      NS_IS_MOUSE_LEFT_CLICK(aVisitor.mEvent) &&
+  // .click(). However, the pickers will follow the rules of popup-blocking.
+  if (NS_IS_MOUSE_LEFT_CLICK(aVisitor.mEvent) &&
       !aVisitor.mEvent->mFlags.mDefaultPrevented) {
-    FireAsyncClickHandler();
-  }
+    if (mType == NS_FORM_INPUT_FILE) {
+      return InitFilePicker();
+    }
+    if (mType == NS_FORM_INPUT_COLOR) {
+      return InitColorPicker();
+    }
+  }
+  return NS_OK;
 }
 
 nsresult
 HTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
   if (!aVisitor.mPresContext) {
     // Hack alert! In order to open file picker even in case the element isn't
-    // in document, fire click handler even without PresContext.
-    MaybeFireAsyncClickHandler(aVisitor);
-    return NS_OK;
+    // in document, try to init picker even without PresContext.
+    return MaybeInitPickers(aVisitor);
   }
 
   if (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
       aVisitor.mEvent->message == NS_BLUR_CONTENT) {
     if (aVisitor.mEvent->message == NS_FOCUS_CONTENT &&
         MayFireChangeOnBlur()) {
       GetValueInternal(mFocusedValue);
     }
@@ -3328,19 +3304,17 @@ HTMLInputElement::PostHandleEvent(nsEven
       mForm->FlushPendingSubmission();
     }
   } // if
 
   if (NS_SUCCEEDED(rv) && mType == NS_FORM_INPUT_RANGE) {
     PostHandleEventForRangeThumb(aVisitor);
   }
 
-  MaybeFireAsyncClickHandler(aVisitor);
-
-  return rv;
+  return MaybeInitPickers(aVisitor);
 }
 
 void
 HTMLInputElement::PostHandleEventForRangeThumb(nsEventChainPostVisitor& aVisitor)
 {
   MOZ_ASSERT(mType == NS_FORM_INPUT_RANGE);
 
   if (nsEventStatus_eConsumeNoDefault == aVisitor.mEventStatus ||
--- a/content/html/content/src/HTMLInputElement.h
+++ b/content/html/content/src/HTMLInputElement.h
@@ -217,19 +217,16 @@ public:
    * button in the group.
    *
    * @return the selected button (or null).
    */
   already_AddRefed<nsIDOMHTMLInputElement> GetSelectedRadioButton();
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
-  void MaybeFireAsyncClickHandler(nsEventChainPostVisitor& aVisitor);
-  NS_IMETHOD FireAsyncClickHandler();
-
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLInputElement,
                                            nsGenericHTMLFormElementWithState)
 
   static UploadLastDir* gUploadLastDir;
   // create and destroy the static UploadLastDir object for remembering
   // which directory was last used on a site-by-site basis
   static void InitUploadLastDir();
   static void DestroyUploadLastDir();
@@ -1078,16 +1075,35 @@ protected:
 
   /**
    * Returns true if the element should prevent dispatching another DOMActivate.
    * This is used in situations where the anonymous subtree should already have
    * sent a DOMActivate and prevents firing more than once.
    */
   bool ShouldPreventDOMActivateDispatch(EventTarget* aOriginalTarget);
 
+  /**
+   * Some input type (color and file) let user choose a value using a picker:
+   * this function checks if it is needed, and if so, open the corresponding
+   * picker (color picker or file picker).
+   */
+  nsresult MaybeInitPickers(nsEventChainPostVisitor& aVisitor);
+
+  nsresult InitFilePicker();
+  nsresult InitColorPicker();
+
+  /**
+   * Use this function before trying to open a picker.
+   * It checks if the page is allowed to open a new pop-up.
+   * If it returns true, you should not create the picker.
+   *
+   * @return true if popup should be blocked, false otherwise
+   */
+  bool IsPopupBlocked() const;
+
   nsCOMPtr<nsIControllers> mControllers;
 
   /*
    * In mInputData, the mState field is used if IsSingleLineTextControl returns
    * true and mValue is used otherwise.  We have to be careful when handling it
    * on a type change.
    *
    * Accessing the mState member should be done using the GetEditorState function,
@@ -1225,31 +1241,16 @@ private:
     // mIsTrusted is true if mime type comes from a "trusted" source (e.g. our
     // hard-coded set).
     // false means it may come from an "untrusted" source (e.g. OS mime types
     // mapping, which can be different accross OS, user's personal configuration, ...)
     // For now, only mask filters are considered to be "trusted".
     bool mIsTrusted; 
   };
 
-  class AsyncClickHandler
-    : public nsRunnable
-  {
-  public:
-    AsyncClickHandler(HTMLInputElement* aInput);
-    NS_IMETHOD Run() MOZ_OVERRIDE;
-
-  protected:
-    nsresult InitFilePicker();
-    nsresult InitColorPicker();
-
-    nsRefPtr<HTMLInputElement> mInput;
-    PopupControlState mPopupControlState;
-  };
-
   class nsFilePickerShownCallback
     : public nsIFilePickerShownCallback
   {
   public:
     nsFilePickerShownCallback(HTMLInputElement* aInput,
                               nsIFilePicker* aFilePicker,
                               bool aMulti);
     virtual ~nsFilePickerShownCallback()
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -938,17 +938,17 @@ nsXBLBinding::DoInitJSClass(JSContext *c
         className.Append(buf);
       }
     }
   }
 
   JS::Rooted<JSObject*> proto(cx);
   JS::Rooted<JS::Value> val(cx);
 
-  if (!::JS_LookupPropertyWithFlags(cx, global, className.get(), 0, val.address()))
+  if (!::JS_LookupPropertyWithFlags(cx, global, className.get(), 0, &val))
     return NS_ERROR_OUT_OF_MEMORY;
 
   if (val.isObject()) {
     *aNew = false;
     proto = &val.toObject();
   } else {
     // We need to initialize the class.
     *aNew = true;
--- a/content/xbl/src/nsXBLProtoImpl.cpp
+++ b/content/xbl/src/nsXBLProtoImpl.cpp
@@ -279,17 +279,17 @@ nsXBLProtoImpl::ResolveAllFields(JSConte
   for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) {
     // Using OBJ_LOOKUP_PROPERTY is a pain, since what we have is a
     // PRUnichar* for the property name.  Let's just use the public API and
     // all.
     nsDependentString name(f->GetName());
     JS::Rooted<JS::Value> dummy(cx);
     if (!::JS_LookupUCProperty(cx, obj,
                                reinterpret_cast<const jschar*>(name.get()),
-                               name.Length(), dummy.address())) {
+                               name.Length(), &dummy)) {
       return false;
     }
   }
 
   return true;
 }
 
 void
@@ -299,17 +299,17 @@ nsXBLProtoImpl::UndefineFields(JSContext
   for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) {
     nsDependentString name(f->GetName());
 
     const jschar* s = reinterpret_cast<const jschar*>(name.get());
     JSBool hasProp;
     if (::JS_AlreadyHasOwnUCProperty(cx, obj, s, name.Length(), &hasProp) &&
         hasProp) {
       JS::Rooted<JS::Value> dummy(cx);
-      ::JS_DeleteUCProperty2(cx, obj, s, name.Length(), dummy.address());
+      ::JS_DeleteUCProperty2(cx, obj, s, name.Length(), &dummy);
     }
   }
 }
 
 void
 nsXBLProtoImpl::DestroyMembers()
 {
   NS_ASSERTION(mClassObject, "This should never be called when there is no class object");
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1927,17 +1927,17 @@ nsDOMClassInfo::NewEnumerate(nsIXPConnec
 nsresult
 nsDOMClassInfo::ResolveConstructor(JSContext *cx, JSObject *aObj,
                                    JSObject **objp)
 {
   JS::Rooted<JSObject*> obj(cx, aObj);
   JS::Rooted<JSObject*> global(cx, ::JS_GetGlobalForObject(cx, obj));
 
   JS::Rooted<JS::Value> val(cx);
-  if (!::JS_LookupProperty(cx, global, mData->mName, val.address())) {
+  if (!::JS_LookupProperty(cx, global, mData->mName, &val)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!JSVAL_IS_PRIMITIVE(val)) {
     // If val is not an (non-null) object there either is no
     // constructor for this class, or someone messed with
     // window.classname, just fall through and let the JS engine
     // return the Object constructor.
@@ -2596,17 +2596,17 @@ struct ResolveGlobalNameClosure
 static PLDHashOperator
 ResolveGlobalName(const nsAString& aName, void* aClosure)
 {
   ResolveGlobalNameClosure* closure =
     static_cast<ResolveGlobalNameClosure*>(aClosure);
   JS::Rooted<JS::Value> dummy(closure->cx);
   bool ok = JS_LookupUCProperty(closure->cx, closure->obj,
                                 aName.BeginReading(), aName.Length(),
-                                dummy.address());
+                                &dummy);
   if (!ok) {
     *closure->retval = false;
     return PL_DHASH_STOP;
   }
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
@@ -3406,22 +3406,22 @@ ResolvePrototype(nsIXPConnect *aXPConnec
     JS::Rooted<JSObject*> winobj(cx, aWin->FastGetGlobalJSObject());
 
     JS::Rooted<JSObject*> proto(cx);
 
     if (class_parent_name) {
       JSAutoCompartment ac(cx, winobj);
 
       JS::Rooted<JS::Value> val(cx);
-      if (!JS_LookupProperty(cx, winobj, CutPrefix(class_parent_name), val.address())) {
+      if (!JS_LookupProperty(cx, winobj, CutPrefix(class_parent_name), &val)) {
         return NS_ERROR_UNEXPECTED;
       }
 
       if (val.isObject()) {
-        if (!JS_LookupProperty(cx, &val.toObject(), "prototype", val.address())) {
+        if (!JS_LookupProperty(cx, &val.toObject(), "prototype", &val)) {
           return NS_ERROR_UNEXPECTED;
         }
 
         if (val.isObject()) {
           proto = &val.toObject();
         }
       }
     }
@@ -4228,17 +4228,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
           *_retval = JS_FALSE;
           return NS_OK;
       }
       if (proto) {
         JS::Rooted<JSObject*> pobj(cx);
         JS::Rooted<JS::Value> val(cx);
 
         if (!::JS_LookupPropertyWithFlagsById(cx, proto, id, flags,
-                                              pobj.address(), val.address())) {
+                                              pobj.address(), &val)) {
           *_retval = JS_FALSE;
 
           return NS_OK;
         }
 
         if (pobj) {
           // A property was found on the prototype chain.
           *objp = pobj;
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -211,17 +211,17 @@ bool
 DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
                          JS::Handle<jsid> id, bool* bp)
 {
   JSBool b = true;
 
   JS::Rooted<JSObject*> expando(cx);
   if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
     JS::Rooted<Value> v(cx);
-    if (!JS_DeletePropertyById2(cx, expando, id, v.address()) ||
+    if (!JS_DeletePropertyById2(cx, expando, id, &v) ||
         !JS_ValueToBoolean(cx, v, &b)) {
       return false;
     }
   }
 
   *bp = !!b;
   return true;
 }
--- a/dom/file/LockedFile.cpp
+++ b/dom/file/LockedFile.cpp
@@ -518,17 +518,18 @@ LockedFile::SetLocation(JSContext* aCx,
 
   // Null means the end-of-file.
   if (JSVAL_IS_NULL(aLocation)) {
     mLocation = UINT64_MAX;
     return NS_OK;
   }
 
   uint64_t location;
-  if (!JS::ToUint64(aCx, aLocation, &location)) {
+  JS::Rooted<JS::Value> value(aCx, aLocation);
+  if (!JS::ToUint64(aCx, value, &location)) {
     return NS_ERROR_TYPE_ERR;
   }
 
   mLocation = location;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/indexedDB/KeyPath.cpp
+++ b/dom/indexedDB/KeyPath.cpp
@@ -203,17 +203,17 @@ GetJSValFromKeyPathString(JSContext* aCx
 
   if (targetObject) {
     // If this fails, we lose, and the web page sees a magical property
     // appear on the object :-(
     JS::Rooted<JS::Value> succeeded(aCx);
     if (!JS_DeleteUCProperty2(aCx, targetObject,
                               targetObjectPropName.get(),
                               targetObjectPropName.Length(),
-                              succeeded.address())) {
+                              &succeeded)) {
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
     NS_ASSERTION(JSVAL_IS_BOOLEAN(succeeded), "Wtf?");
     NS_ENSURE_TRUE(JSVAL_TO_BOOLEAN(succeeded),
                    NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -818,17 +818,17 @@ nsJSObjWrapper::NP_RemoveProperty(NPObje
   nsCxPusher pusher;
   pusher.Push(cx);
   AutoJSExceptionReporter reporter(cx);
   JS::Rooted<JS::Value> deleted(cx, JSVAL_FALSE);
   JSAutoCompartment ac(cx, npjsobj->mJSObj);
 
   NS_ASSERTION(NPIdentifierIsInt(id) || NPIdentifierIsString(id),
                "id must be either string or int!\n");
-  ok = ::JS_DeletePropertyById2(cx, npjsobj->mJSObj, NPIdentifierToJSId(id), deleted.address());
+  ok = ::JS_DeletePropertyById2(cx, npjsobj->mJSObj, NPIdentifierToJSId(id), &deleted);
   if (ok && deleted == JSVAL_TRUE) {
     // FIXME: See bug 425823, we shouldn't need to do this, and once
     // that bug is fixed we can remove this code.
 
     JSBool hasProp;
     ok = ::JS_HasPropertyById(cx, npjsobj->mJSObj, NPIdentifierToJSId(id), &hasProp);
 
     if (ok && hasProp) {
--- a/dom/workers/Worker.cpp
+++ b/dom/workers/Worker.cpp
@@ -137,23 +137,24 @@ protected:
       parent->AssertIsOnWorkerThread();
     }
 
     JS::Rooted<JSObject*> obj(aCx, JS_NewObject(aCx, aClass, nullptr, nullptr));
     if (!obj) {
       return false;
     }
 
+    // Ensure that the DOM_OBJECT_SLOT always has a PrivateValue set, as this
+    // will be accessed in the Trace() method if WorkerPrivate::Create()
+    // triggers a GC.
+    js::SetReservedSlot(obj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
+
     nsRefPtr<WorkerPrivate> worker =
       WorkerPrivate::Create(aCx, obj, parent, scriptURL, aIsChromeWorker);
     if (!worker) {
-      // It'd be better if we could avoid allocating the JSObject until after we
-      // make sure we have a WorkerPrivate, but failing that we should at least
-      // make sure that the DOM_OBJECT_SLOT always has a PrivateValue.
-      js::SetReservedSlot(obj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
       return false;
     }
 
     // Worker now owned by the JS object.
     NS_ADDREF(worker.get());
     js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(worker));
 
     if (!runtimeService->RegisterWorker(aCx, worker)) {
--- a/js/ipc/JavaScriptChild.cpp
+++ b/js/ipc/JavaScriptChild.cpp
@@ -279,17 +279,17 @@ JavaScriptChild::AnswerDelete(const Obje
 
     JSAutoCompartment comp(cx, obj);
 
     RootedId internedId(cx);
     if (!convertGeckoStringToId(cx, id, &internedId))
         return fail(cx, rs);
 
     RootedValue v(cx);
-    if (!JS_DeletePropertyById2(cx, obj, internedId, v.address()))
+    if (!JS_DeletePropertyById2(cx, obj, internedId, &v))
         return fail(cx, rs);
 
     JSBool b;
     if (!JS_ValueToBoolean(cx, v, &b))
         return fail(cx, rs);
     *success = !!b;
 
     return ok(rs);
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -503,19 +503,23 @@ class MOZ_STACK_CLASS MutableHandle : pu
      */
     static MutableHandle fromMarkedLocation(T *p) {
         MutableHandle h;
         h.ptr = p;
         return h;
     }
 
     T *address() const { return ptr; }
-    T get() const { return *ptr; }
+    const T& get() const { return *ptr; }
 
-    operator T() const { return get(); }
+    /*
+     * Return a reference so passing a MutableHandle<T> to something that takes
+     * a |const T&| is not a GC hazard.
+     */
+    operator const T&() const { return get(); }
     T operator->() const { return get(); }
 
   private:
     MutableHandle() {}
 
     T *ptr;
 
     template <typename S> void operator=(S v) MOZ_DELETE;
--- a/js/src/ion/AsmJS.cpp
+++ b/js/src/ion/AsmJS.cpp
@@ -5704,22 +5704,22 @@ GenerateFFIInterpreterExit(ModuleCompile
     }
 
     masm.freeStack(reserveSize + sizeof(int32_t));
     masm.ret();
 #endif
 }
 
 static int32_t
-ValueToInt32(JSContext *cx, Value *val)
+ValueToInt32(JSContext *cx, MutableHandleValue val)
 {
     int32_t i32;
-    if (!ToInt32(cx, val[0], &i32))
+    if (!ToInt32(cx, val, &i32))
         return false;
-    val[0] = Int32Value(i32);
+    val.set(Int32Value(i32));
 
     return true;
 }
 
 static int32_t
 ValueToNumber(JSContext *cx, MutableHandleValue val)
 {
     double dbl;
--- a/js/src/jsapi-tests/testLookup.cpp
+++ b/js/src/jsapi-tests/testLookup.cpp
@@ -22,17 +22,17 @@ BEGIN_TEST(testLookup_bug522590)
     CHECK_SAME(x, JSVAL_TRUE);
 
     // Now make x.f a method.
     EVAL("mkobj()", x.address());
     JS::RootedObject xobj(cx, JSVAL_TO_OBJECT(x));
 
     // This lookup must not return an internal function object.
     JS::RootedValue r(cx);
-    CHECK(JS_LookupProperty(cx, xobj, "f", r.address()));
+    CHECK(JS_LookupProperty(cx, xobj, "f", &r));
     CHECK(r.isObject());
     JSObject *funobj = &r.toObject();
     CHECK(funobj->is<JSFunction>());
     CHECK(!js::IsInternalFunctionObject(funobj));
 
     return true;
 }
 END_TEST(testLookup_bug522590)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3101,52 +3101,52 @@ LookupPropertyById(JSContext *cx, Handle
     JSAutoResolveFlags rf(cx, flags);
     return JSObject::lookupGeneric(cx, obj, id, objp, propp);
 }
 
 #define AUTO_NAMELEN(s,n)   (((n) == (size_t)-1) ? js_strlen(s) : (n))
 
 static JSBool
 LookupResult(JSContext *cx, HandleObject obj, HandleObject obj2, HandleId id,
-             HandleShape shape, Value *vp)
+             HandleShape shape, MutableHandleValue vp)
 {
     if (!shape) {
         /* XXX bad API: no way to tell "not defined" from "void value" */
-        vp->setUndefined();
+        vp.setUndefined();
         return JS_TRUE;
     }
 
     if (!obj2->isNative()) {
         if (obj2->is<ProxyObject>()) {
             AutoPropertyDescriptorRooter desc(cx);
             if (!Proxy::getPropertyDescriptor(cx, obj2, id, &desc, 0))
                 return false;
             if (!(desc.attrs & JSPROP_SHARED)) {
-                *vp = desc.value;
+                vp.set(desc.value);
                 return true;
             }
         }
     } else if (IsImplicitDenseElement(shape)) {
-        *vp = obj2->getDenseElement(JSID_TO_INT(id));
+        vp.set(obj2->getDenseElement(JSID_TO_INT(id)));
         return true;
     } else {
         /* Peek at the native property's slot value, without doing a Get. */
         if (shape->hasSlot()) {
-            *vp = obj2->nativeGetSlot(shape->slot());
+            vp.set(obj2->nativeGetSlot(shape->slot()));
             return true;
         }
     }
 
     /* XXX bad API: no way to return "defined but value unknown" */
-    vp->setBoolean(true);
+    vp.setBoolean(true);
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
-JS_LookupPropertyById(JSContext *cx, JSObject *objArg, jsid idArg, jsval *vp)
+JS_LookupPropertyById(JSContext *cx, JSObject *objArg, jsid idArg, MutableHandleValue vp)
 {
     RootedId id(cx, idArg);
     RootedObject obj(cx, objArg);
     RootedObject obj2(cx);
     RootedShape prop(cx);
 
     return LookupPropertyById(cx, obj, id, 0, &obj2, &prop) &&
            LookupResult(cx, obj, obj2, id, prop, vp);
@@ -3155,38 +3155,43 @@ JS_LookupPropertyById(JSContext *cx, JSO
 JS_PUBLIC_API(JSBool)
 JS_LookupElement(JSContext *cx, JSObject *objArg, uint32_t index, jsval *vp)
 {
     RootedObject obj(cx, objArg);
     CHECK_REQUEST(cx);
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
-    return JS_LookupPropertyById(cx, obj, id, vp);
-}
-
-JS_PUBLIC_API(JSBool)
-JS_LookupProperty(JSContext *cx, JSObject *objArg, const char *name, jsval *vp)
+    RootedValue value(cx);
+    if (!JS_LookupPropertyById(cx, obj, id, &value))
+        return false;
+    *vp = value;
+    return true;
+}
+
+JS_PUBLIC_API(JSBool)
+JS_LookupProperty(JSContext *cx, JSObject *objArg, const char *name, MutableHandleValue vp)
 {
     RootedObject obj(cx, objArg);
     JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_LookupPropertyById(cx, obj, AtomToId(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_LookupUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen, jsval *vp)
+JS_LookupUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen,
+                    MutableHandleValue vp)
 {
     RootedObject obj(cx, objArg);
     JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_LookupPropertyById(cx, obj, AtomToId(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_LookupPropertyWithFlagsById(JSContext *cx, JSObject *objArg, jsid id_, unsigned flags,
-                               JSObject **objpArg, jsval *vp)
+                               JSObject **objpArg, MutableHandleValue vp)
 {
     RootedObject obj(cx, objArg);
     RootedObject objp(cx, *objpArg);
     RootedId id(cx, id_);
     RootedShape prop(cx);
 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
@@ -3199,17 +3204,18 @@ JS_LookupPropertyWithFlagsById(JSContext
     if (!LookupResult(cx, obj, objp, id, prop, vp))
         return false;
 
     *objpArg = objp;
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
-JS_LookupPropertyWithFlags(JSContext *cx, JSObject *objArg, const char *name, unsigned flags, jsval *vp)
+JS_LookupPropertyWithFlags(JSContext *cx, JSObject *objArg, const char *name, unsigned flags,
+                           MutableHandleValue vp)
 {
     RootedObject obj(cx, objArg);
     JSObject *obj2;
     JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_LookupPropertyWithFlagsById(cx, obj, AtomToId(atom), flags, &obj2, vp);
 }
 
 JS_PUBLIC_API(JSBool)
@@ -3713,28 +3719,24 @@ JS_GetUCPropertyAttrsGetterAndSetter(JSC
 {
     RootedObject obj(cx, objArg);
     JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_GetPropertyAttrsGetterAndSetterById(cx, obj, AtomToId(atom),
                                                           attrsp, foundp, getterp, setterp);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_GetOwnPropertyDescriptor(JSContext *cx, JSObject *objArg, jsid idArg, jsval *vp)
+JS_GetOwnPropertyDescriptor(JSContext *cx, JSObject *objArg, jsid idArg, MutableHandleValue vp)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
-    RootedValue value(cx);
-    if (!GetOwnPropertyDescriptor(cx, obj, id, &value))
-        return false;
-    *vp = value;
-    return true;
+    return GetOwnPropertyDescriptor(cx, obj, id, vp);
 }
 
 static JSBool
 SetPropertyAttributesById(JSContext *cx, HandleObject obj, HandleId id, unsigned attrs, JSBool *foundp)
 {
     RootedObject obj2(cx);
     RootedShape shape(cx);
 
@@ -3921,17 +3923,17 @@ JS_SetUCProperty(JSContext *cx, JSObject
                  HandleValue v)
 {
     RootedObject obj(cx, objArg);
     JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_SetPropertyById(cx, obj, AtomToId(atom), v);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_DeletePropertyById2(JSContext *cx, JSObject *objArg, jsid id, jsval *rval)
+JS_DeletePropertyById2(JSContext *cx, JSObject *objArg, jsid id, MutableHandleValue rval)
 {
     RootedObject obj(cx, objArg);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
     JSAutoResolveFlags rf(cx, 0);
 
     RootedValue value(cx);
@@ -3941,17 +3943,17 @@ JS_DeletePropertyById2(JSContext *cx, JS
         Rooted<SpecialId> sid(cx, JSID_TO_SPECIALID(id));
         if (!JSObject::deleteSpecial(cx, obj, sid, &succeeded))
             return false;
     } else {
         if (!JSObject::deleteByValue(cx, obj, IdToValue(id), &succeeded))
             return false;
     }
 
-    *rval = BooleanValue(succeeded);
+    rval.setBoolean(succeeded);
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DeleteElement2(JSContext *cx, JSObject *objArg, uint32_t index, jsval *rval)
 {
     RootedObject obj(cx, objArg);
     AssertHeapIsIdle(cx);
@@ -3963,73 +3965,74 @@ JS_DeleteElement2(JSContext *cx, JSObjec
     if (!JSObject::deleteElement(cx, obj, index, &succeeded))
         return false;
 
     *rval = BooleanValue(succeeded);
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
-JS_DeleteProperty2(JSContext *cx, JSObject *objArg, const char *name, jsval *rval)
+JS_DeleteProperty2(JSContext *cx, JSObject *objArg, const char *name, MutableHandleValue rval)
 {
     RootedObject obj(cx, objArg);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAutoResolveFlags rf(cx, 0);
 
     JSAtom *atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
 
     JSBool succeeded;
     if (!JSObject::deleteByValue(cx, obj, StringValue(atom), &succeeded))
         return false;
 
-    *rval = BooleanValue(succeeded);
+    rval.setBoolean(succeeded);
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
-JS_DeleteUCProperty2(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen, jsval *rval)
+JS_DeleteUCProperty2(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen,
+                     MutableHandleValue rval)
 {
     RootedObject obj(cx, objArg);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAutoResolveFlags rf(cx, 0);
 
     JSAtom *atom = AtomizeChars<CanGC>(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return false;
 
     JSBool succeeded;
     if (!JSObject::deleteByValue(cx, obj, StringValue(atom), &succeeded))
         return false;
 
-    *rval = BooleanValue(succeeded);
+    rval.setBoolean(succeeded);
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DeletePropertyById(JSContext *cx, JSObject *objArg, jsid idArg)
 {
-    jsval junk;
+    RootedValue junk(cx);
     return JS_DeletePropertyById2(cx, objArg, idArg, &junk);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DeleteElement(JSContext *cx, JSObject *objArg, uint32_t index)
 {
     jsval junk;
     return JS_DeleteElement2(cx, objArg, index, &junk);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DeleteProperty(JSContext *cx, JSObject *objArg, const char *name)
 {
-    jsval junk;
+    RootedValue junk(cx);
     return JS_DeleteProperty2(cx, objArg, name, &junk);
 }
 
 static Shape *
 LastConfigurableShape(JSObject *obj)
 {
     for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) {
         Shape *shape = &r.front();
@@ -6769,17 +6772,17 @@ AutoGCRooter::AutoGCRooter(ContextFriend
     stackTop(&cx->autoGCRooters)
 {
     JS_ASSERT(this != *stackTop);
     *stackTop = this;
 }
 
 #ifdef DEBUG
 JS_PUBLIC_API(void)
-JS::AssertArgumentsAreSane(JSContext *cx, const JS::Value &value)
+JS::AssertArgumentsAreSane(JSContext *cx, HandleValue value)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, value);
 }
 #endif /* DEBUG */
 
 JS_PUBLIC_API(void *)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -70,19 +70,19 @@ class JS_PUBLIC_API(AutoCheckRequestDept
 
 #ifdef DEBUG
 /*
  * Assert that we're not doing GC on cx, that we're in a request as
  * needed, and that the compartments for cx and v are correct.
  * Also check that GC would be safe at this point.
  */
 JS_PUBLIC_API(void)
-AssertArgumentsAreSane(JSContext *cx, const Value &v);
+AssertArgumentsAreSane(JSContext *cx, JS::Handle<JS::Value> v);
 #else
-inline void AssertArgumentsAreSane(JSContext *cx, const Value &v) {
+inline void AssertArgumentsAreSane(JSContext *cx, JS::Handle<JS::Value> v) {
     /* Do nothing */
 }
 #endif /* DEBUG */
 
 class JS_PUBLIC_API(AutoGCRooter) {
   public:
     AutoGCRooter(JSContext *cx, ptrdiff_t tag);
     AutoGCRooter(js::ContextFriendFields *cx, ptrdiff_t tag);
@@ -1595,110 +1595,95 @@ JS_ValueToInt64(JSContext *cx, jsval v, 
  * rules for ToUint64: http://dev.w3.org/2006/webapi/WebIDL/#es-unsigned-long-long
  */
 extern JS_PUBLIC_API(JSBool)
 JS_ValueToUint64(JSContext *cx, jsval v, uint64_t *ip);
 
 namespace js {
 /* DO NOT CALL THIS.  Use JS::ToInt16. */
 extern JS_PUBLIC_API(bool)
-ToUint16Slow(JSContext *cx, const JS::Value &v, uint16_t *out);
+ToUint16Slow(JSContext *cx, JS::Handle<JS::Value> v, uint16_t *out);
 
 /* DO NOT CALL THIS.  Use JS::ToInt32. */
 extern JS_PUBLIC_API(bool)
-ToInt32Slow(JSContext *cx, const JS::Value &v, int32_t *out);
+ToInt32Slow(JSContext *cx, JS::Handle<JS::Value> v, int32_t *out);
 
 /* DO NOT CALL THIS.  Use JS::ToUint32. */
 extern JS_PUBLIC_API(bool)
-ToUint32Slow(JSContext *cx, const JS::Value &v, uint32_t *out);
+ToUint32Slow(JSContext *cx, JS::Handle<JS::Value> v, uint32_t *out);
 
 /* DO NOT CALL THIS. Use JS::ToInt64. */
 extern JS_PUBLIC_API(bool)
-ToInt64Slow(JSContext *cx, const JS::Value &v, int64_t *out);
+ToInt64Slow(JSContext *cx, JS::Handle<JS::Value> v, int64_t *out);
 
 /* DO NOT CALL THIS. Use JS::ToUint64. */
 extern JS_PUBLIC_API(bool)
-ToUint64Slow(JSContext *cx, const JS::Value &v, uint64_t *out);
+ToUint64Slow(JSContext *cx, JS::Handle<JS::Value> v, uint64_t *out);
 } /* namespace js */
 
 namespace JS {
 
 JS_ALWAYS_INLINE bool
-ToUint16(JSContext *cx, const JS::Value &v, uint16_t *out)
+ToUint16(JSContext *cx, JS::Handle<JS::Value> v, uint16_t *out)
 {
     AssertArgumentsAreSane(cx, v);
-    {
-        js::SkipRoot skip(cx, &v);
-        js::MaybeCheckStackRoots(cx);
-    }
+    js::MaybeCheckStackRoots(cx);
 
     if (v.isInt32()) {
         *out = uint16_t(v.toInt32());
         return true;
     }
     return js::ToUint16Slow(cx, v, out);
 }
 
 JS_ALWAYS_INLINE bool
-ToInt32(JSContext *cx, const JS::Value &v, int32_t *out)
+ToInt32(JSContext *cx, JS::Handle<JS::Value> v, int32_t *out)
 {
     AssertArgumentsAreSane(cx, v);
-    {
-        js::SkipRoot root(cx, &v);
-        js::MaybeCheckStackRoots(cx);
-    }
+    js::MaybeCheckStackRoots(cx);
 
     if (v.isInt32()) {
         *out = v.toInt32();
         return true;
     }
     return js::ToInt32Slow(cx, v, out);
 }
 
 JS_ALWAYS_INLINE bool
-ToUint32(JSContext *cx, const JS::Value &v, uint32_t *out)
+ToUint32(JSContext *cx, JS::Handle<JS::Value> v, uint32_t *out)
 {
     AssertArgumentsAreSane(cx, v);
-    {
-        js::SkipRoot root(cx, &v);
-        js::MaybeCheckStackRoots(cx);
-    }
+    js::MaybeCheckStackRoots(cx);
 
     if (v.isInt32()) {
         *out = uint32_t(v.toInt32());
         return true;
     }
     return js::ToUint32Slow(cx, v, out);
 }
 
 JS_ALWAYS_INLINE bool
-ToInt64(JSContext *cx, const JS::Value &v, int64_t *out)
+ToInt64(JSContext *cx, JS::Handle<JS::Value> v, int64_t *out)
 {
     AssertArgumentsAreSane(cx, v);
-    {
-        js::SkipRoot skip(cx, &v);
-        js::MaybeCheckStackRoots(cx);
-    }
+    js::MaybeCheckStackRoots(cx);
 
     if (v.isInt32()) {
         *out = int64_t(v.toInt32());
         return true;
     }
 
     return js::ToInt64Slow(cx, v, out);
 }
 
 JS_ALWAYS_INLINE bool
-ToUint64(JSContext *cx, const JS::Value &v, uint64_t *out)
+ToUint64(JSContext *cx, JS::Handle<JS::Value> v, uint64_t *out)
 {
     AssertArgumentsAreSane(cx, v);
-    {
-        js::SkipRoot skip(cx, &v);
-        js::MaybeCheckStackRoots(cx);
-    }
+    js::MaybeCheckStackRoots(cx);
 
     if (v.isInt32()) {
         /* Account for sign extension of negatives into the longer 64bit space. */
         *out = uint64_t(int64_t(v.toInt32()));
         return true;
     }
 
     return js::ToUint64Slow(cx, v, out);
@@ -3399,28 +3384,28 @@ JS_AlreadyHasOwnPropertyById(JSContext *
 
 extern JS_PUBLIC_API(JSBool)
 JS_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_HasPropertyById(JSContext *cx, JSObject *obj, jsid id, JSBool *foundp);
 
 extern JS_PUBLIC_API(JSBool)
-JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp);
+JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, JS::MutableHandle<JS::Value> vp);
 
 extern JS_PUBLIC_API(JSBool)
-JS_LookupPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
+JS_LookupPropertyById(JSContext *cx, JSObject *obj, jsid id, JS::MutableHandle<JS::Value> vp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name,
-                           unsigned flags, jsval *vp);
+                           unsigned flags, JS::MutableHandle<JS::Value> vp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_LookupPropertyWithFlagsById(JSContext *cx, JSObject *obj, jsid id,
-                               unsigned flags, JSObject **objp, jsval *vp);
+                               unsigned flags, JSObject **objp, JS::MutableHandle<JS::Value> vp);
 
 struct JSPropertyDescriptor {
     JSObject           *obj;
     unsigned           attrs;
     unsigned           shortid;
     JSPropertyOp       getter;
     JSStrictPropertyOp setter;
     JS::Value          value;
@@ -3540,17 +3525,17 @@ class MutableHandleBase<JSPropertyDescri
  * an object on the prototype chain (returned in objp). If data->obj is null,
  * then this property was not found on the prototype chain.
  */
 extern JS_PUBLIC_API(JSBool)
 JS_GetPropertyDescriptorById(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
                              JSPropertyDescriptor *desc);
 
 extern JS_PUBLIC_API(JSBool)
-JS_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
+JS_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, JS::MutableHandle<JS::Value> vp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, JS::MutableHandle<JS::Value> vp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_GetPropertyDefault(JSContext *cx, JSObject *obj, const char *name, jsval def,
                       JS::MutableHandle<JS::Value> vp);
 
@@ -3571,23 +3556,23 @@ JS_SetProperty(JSContext *cx, JSObject *
 extern JS_PUBLIC_API(JSBool)
 JS_SetPropertyById(JSContext *cx, JSObject *obj, jsid id, JS::Handle<JS::Value> v);
 
 extern JS_PUBLIC_API(JSBool)
 JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name);
 
 extern JS_PUBLIC_API(JSBool)
 JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name,
-                   jsval *rval);
+                   JS::MutableHandle<JS::Value> rval);
 
 extern JS_PUBLIC_API(JSBool)
 JS_DeletePropertyById(JSContext *cx, JSObject *obj, jsid id);
 
 extern JS_PUBLIC_API(JSBool)
-JS_DeletePropertyById2(JSContext *cx, JSObject *obj, jsid id, jsval *rval);
+JS_DeletePropertyById2(JSContext *cx, JSObject *obj, jsid id, JS::MutableHandle<JS::Value> rval);
 
 extern JS_PUBLIC_API(JSBool)
 JS_DefineUCProperty(JSContext *cx, JSObject *obj,
                     const jschar *name, size_t namelen, jsval value,
                     JSPropertyOp getter, JSStrictPropertyOp setter,
                     unsigned attrs);
 
 /*
@@ -3639,32 +3624,32 @@ JS_AlreadyHasOwnUCProperty(JSContext *cx
 extern JS_PUBLIC_API(JSBool)
 JS_HasUCProperty(JSContext *cx, JSObject *obj,
                  const jschar *name, size_t namelen,
                  JSBool *vp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_LookupUCProperty(JSContext *cx, JSObject *obj,
                     const jschar *name, size_t namelen,
-                    jsval *vp);
+                    JS::MutableHandle<JS::Value> vp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_GetUCProperty(JSContext *cx, JSObject *obj,
                  const jschar *name, size_t namelen,
                  JS::MutableHandle<JS::Value> vp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_SetUCProperty(JSContext *cx, JSObject *obj,
                  const jschar *name, size_t namelen,
                  JS::Handle<JS::Value> v);
 
 extern JS_PUBLIC_API(JSBool)
 JS_DeleteUCProperty2(JSContext *cx, JSObject *obj,
                      const jschar *name, size_t namelen,
-                     jsval *rval);
+                     JS::MutableHandle<JS::Value> rval);
 
 extern JS_PUBLIC_API(JSObject *)
 JS_NewArrayObject(JSContext *cx, int length, jsval *vector);
 
 extern JS_PUBLIC_API(JSBool)
 JS_IsArrayObject(JSContext *cx, JSObject *obj);
 
 extern JS_PUBLIC_API(JSBool)
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -526,17 +526,17 @@ js::ReportUsageError(JSContext *cx, Hand
     PropertyName *usageAtom = Atomize(cx, usageStr, strlen(usageStr))->asPropertyName();
     RootedId id(cx, NameToId(usageAtom));
     DebugOnly<Shape *> shape = static_cast<Shape *>(callee->nativeLookup(cx, id));
     JS_ASSERT(!shape->configurable());
     JS_ASSERT(!shape->writable());
     JS_ASSERT(shape->hasDefaultGetter());
 
     RootedValue usage(cx);
-    if (!JS_LookupProperty(cx, callee, "usage", usage.address()))
+    if (!JS_LookupProperty(cx, callee, "usage", &usage))
         return;
 
     if (JSVAL_IS_VOID(usage)) {
         JS_ReportError(cx, "%s", msg);
     } else {
         JSString *str = JSVAL_TO_STRING(usage);
         JS::Anchor<JSString *> a_str(str);
         const jschar *chars = JS_GetStringCharsZ(cx, str);
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -1002,28 +1002,27 @@ IsDuckTypedErrorObject(JSContext *cx, Ha
 
     *filename_strp = filename_str;
     return true;
 }
 
 JSBool
 js_ReportUncaughtException(JSContext *cx)
 {
-    jsval roots[6];
     JSErrorReport *reportp, report;
 
     if (!JS_IsExceptionPending(cx))
         return true;
 
     RootedValue exn(cx);
     if (!JS_GetPendingException(cx, exn.address()))
         return false;
 
-    PodArrayZero(roots);
-    AutoArrayRooter tvr(cx, ArrayLength(roots), roots);
+    AutoValueVector roots(cx);
+    roots.resize(6);
 
     /*
      * Because ToString below could error and an exception object could become
      * unrooted, we must root exnObject.  Later, if exnObject is non-null, we
      * need to root other intermediates, so allocate an operand stack segment
      * to protect all of these values.
      */
     RootedObject exnObject(cx);
@@ -1043,28 +1042,22 @@ js_ReportUncaughtException(JSContext *cx
         roots[1] = StringValue(str);
 
     const char *filename_str = js_fileName_str;
     JSAutoByteString filename;
     if (!reportp && exnObject &&
         (exnObject->is<ErrorObject>() || IsDuckTypedErrorObject(cx, exnObject, &filename_str)))
     {
         RootedString name(cx);
-        if (JS_GetProperty(cx, exnObject, js_name_str, tvr.handleAt(2)) &&
-            JSVAL_IS_STRING(roots[2]))
-        {
-            name = JSVAL_TO_STRING(roots[2]);
-        }
+        if (JS_GetProperty(cx, exnObject, js_name_str, roots.handleAt(2)) && roots[2].isString())
+            name = roots[2].toString();
 
         RootedString msg(cx);
-        if (JS_GetProperty(cx, exnObject, js_message_str, tvr.handleAt(3)) &&
-            JSVAL_IS_STRING(roots[3]))
-        {
-            msg = JSVAL_TO_STRING(roots[3]);
-        }
+        if (JS_GetProperty(cx, exnObject, js_message_str, roots.handleAt(3)) && roots[3].isString())
+            msg = roots[3].toString();
 
         if (name && msg) {
             RootedString colon(cx, JS_NewStringCopyZ(cx, ": "));
             if (!colon)
                 return false;
             RootedString nameColon(cx, ConcatStrings<CanGC>(cx, name, colon));
             if (!nameColon)
                 return false;
@@ -1072,32 +1065,32 @@ js_ReportUncaughtException(JSContext *cx
             if (!str)
                 return false;
         } else if (name) {
             str = name;
         } else if (msg) {
             str = msg;
         }
 
-        if (JS_GetProperty(cx, exnObject, filename_str, tvr.handleAt(4))) {
-            JSString *tmp = ToString<CanGC>(cx, HandleValue::fromMarkedLocation(&roots[4]));
+        if (JS_GetProperty(cx, exnObject, filename_str, roots.handleAt(4))) {
+            JSString *tmp = ToString<CanGC>(cx, roots.handleAt(4));
             if (tmp)
                 filename.encodeLatin1(cx, tmp);
         }
 
         uint32_t lineno;
-        if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, tvr.handleAt(5)) ||
-            !ToUint32(cx, roots[5], &lineno))
+        if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, roots.handleAt(5)) ||
+            !ToUint32(cx, roots.handleAt(5), &lineno))
         {
             lineno = 0;
         }
 
         uint32_t column;
-        if (!JS_GetProperty(cx, exnObject, js_columnNumber_str, tvr.handleAt(5)) ||
-            !ToUint32(cx, roots[5], &column))
+        if (!JS_GetProperty(cx, exnObject, js_columnNumber_str, roots.handleAt(5)) ||
+            !ToUint32(cx, roots.handleAt(5), &column))
         {
             column = 0;
         }
 
         reportp = &report;
         PodZero(&report);
         report.filename = filename.ptr();
         report.lineno = (unsigned) lineno;
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1529,17 +1529,17 @@ js::ToNumberSlow(JSContext *cx, Value v,
 # pragma optimize("", on)
 #endif
 
 /*
  * Convert a value to an int64_t, according to the WebIDL rules for long long
  * conversion. Return converted value in *out on success, false on failure.
  */
 JS_PUBLIC_API(bool)
-js::ToInt64Slow(JSContext *cx, const Value &v, int64_t *out)
+js::ToInt64Slow(JSContext *cx, const HandleValue v, int64_t *out)
 {
     JS_ASSERT(!v.isInt32());
     double d;
     if (v.isDouble()) {
         d = v.toDouble();
     } else {
         if (!ToNumberSlow(cx, v, &d))
             return false;
@@ -1548,62 +1548,62 @@ js::ToInt64Slow(JSContext *cx, const Val
     return true;
 }
 
 /*
  * Convert a value to an uint64_t, according to the WebIDL rules for unsigned long long
  * conversion. Return converted value in *out on success, false on failure.
  */
 JS_PUBLIC_API(bool)
-js::ToUint64Slow(JSContext *cx, const Value &v, uint64_t *out)
+js::ToUint64Slow(JSContext *cx, const HandleValue v, uint64_t *out)
 {
     JS_ASSERT(!v.isInt32());
     double d;
     if (v.isDouble()) {
         d = v.toDouble();
     } else {
         if (!ToNumberSlow(cx, v, &d))
             return false;
     }
     *out = ToUint64(d);
     return true;
 }
 
 JS_PUBLIC_API(bool)
-js::ToInt32Slow(JSContext *cx, const Value &v, int32_t *out)
+js::ToInt32Slow(JSContext *cx, const HandleValue v, int32_t *out)
 {
     JS_ASSERT(!v.isInt32());
     double d;
     if (v.isDouble()) {
         d = v.toDouble();
     } else {
         if (!ToNumberSlow(cx, v, &d))
             return false;
     }
     *out = ToInt32(d);
     return true;
 }
 
 JS_PUBLIC_API(bool)
-js::ToUint32Slow(JSContext *cx, const Value &v, uint32_t *out)
+js::ToUint32Slow(JSContext *cx, const HandleValue v, uint32_t *out)
 {
     JS_ASSERT(!v.isInt32());
     double d;
     if (v.isDouble()) {
         d = v.toDouble();
     } else {
         if (!ToNumberSlow(cx, v, &d))
             return false;
     }
     *out = ToUint32(d);
     return true;
 }
 
 JS_PUBLIC_API(bool)
-js::ToUint16Slow(JSContext *cx, const Value &v, uint16_t *out)
+js::ToUint16Slow(JSContext *cx, const HandleValue v, uint16_t *out)
 {
     JS_ASSERT(!v.isInt32());
     double d;
     if (v.isDouble()) {
         d = v.toDouble();
     } else if (!ToNumberSlow(cx, v, &d)) {
         return false;
     }
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -419,17 +419,17 @@ DirectProxyHandler::getOwnPropertyNames(
 }
 
 bool
 DirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
 {
     assertEnteredPolicy(cx, proxy, id);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     RootedValue v(cx);
-    if (!JS_DeletePropertyById2(cx, target, id, v.address()))
+    if (!JS_DeletePropertyById2(cx, target, id, &v))
         return false;
     JSBool b;
     if (!JS_ValueToBoolean(cx, v, &b))
         return false;
     *bp = !!b;
     return true;
 }
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4019,20 +4019,20 @@ PrintHelpString(JSContext *cx, jsval v)
 
     return true;
 }
 
 static bool
 PrintHelp(JSContext *cx, HandleObject obj)
 {
     RootedValue usage(cx);
-    if (!JS_LookupProperty(cx, obj, "usage", usage.address()))
+    if (!JS_LookupProperty(cx, obj, "usage", &usage))
         return false;
     RootedValue help(cx);
-    if (!JS_LookupProperty(cx, obj, "help", help.address()))
+    if (!JS_LookupProperty(cx, obj, "help", &help))
         return false;
 
     if (JSVAL_IS_VOID(usage) || JSVAL_IS_VOID(help))
         return true;
 
     return PrintHelpString(cx, usage) && PrintHelpString(cx, help);
 }
 
@@ -4045,17 +4045,17 @@ Help(JSContext *cx, unsigned argc, jsval
     if (argc == 0) {
         RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
         AutoIdArray ida(cx, JS_Enumerate(cx, global));
         if (!ida)
             return false;
 
         for (size_t i = 0; i < ida.length(); i++) {
             RootedValue v(cx);
-            if (!JS_LookupPropertyById(cx, global, ida[i], v.address()))
+            if (!JS_LookupPropertyById(cx, global, ida[i], &v))
                 return false;
             if (JSVAL_IS_PRIMITIVE(v)) {
                 JS_ReportError(cx, "primitive arg");
                 return false;
             }
             obj = JSVAL_TO_OBJECT(v);
             if (!PrintHelp(cx, obj))
                 return false;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1989,19 +1989,19 @@ BEGIN_CASE(JSOP_BINDNAME)
 
     PUSH_OBJECT(*scope);
 }
 END_CASE(JSOP_BINDNAME)
 
 #define BITWISE_OP(OP)                                                        \
     JS_BEGIN_MACRO                                                            \
         int32_t i, j;                                                         \
-        if (!ToInt32(cx, regs.sp[-2], &i))                                    \
+        if (!ToInt32(cx, regs.stackHandleAt(-2), &i))                         \
             goto error;                                                       \
-        if (!ToInt32(cx, regs.sp[-1], &j))                                    \
+        if (!ToInt32(cx, regs.stackHandleAt(-1), &j))                         \
             goto error;                                                       \
         i = i OP j;                                                           \
         regs.sp--;                                                            \
         regs.sp[-1].setInt32(i);                                              \
     JS_END_MACRO
 
 BEGIN_CASE(JSOP_BITOR)
     BITWISE_OP(|);
@@ -2118,19 +2118,19 @@ BEGIN_CASE(JSOP_GE)
     regs.sp[-2].setBoolean(cond);
     regs.sp--;
 }
 END_CASE(JSOP_GE)
 
 #define SIGNED_SHIFT_OP(OP)                                                   \
     JS_BEGIN_MACRO                                                            \
         int32_t i, j;                                                         \
-        if (!ToInt32(cx, regs.sp[-2], &i))                                    \
+        if (!ToInt32(cx, regs.stackHandleAt(-2), &i))                         \
             goto error;                                                       \
-        if (!ToInt32(cx, regs.sp[-1], &j))                                    \
+        if (!ToInt32(cx, regs.stackHandleAt(-1), &j))                         \
             goto error;                                                       \
         i = i OP (j & 31);                                                    \
         regs.sp--;                                                            \
         regs.sp[-1].setInt32(i);                                              \
     JS_END_MACRO
 
 BEGIN_CASE(JSOP_LSH)
     SIGNED_SHIFT_OP(<<);
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -184,33 +184,34 @@ ArrayBufferObject::fun_slice(JSContext *
 
 /*
  * new ArrayBuffer(byteLength)
  */
 JSBool
 ArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
 {
     int32_t nbytes = 0;
-    if (argc > 0 && !ToInt32(cx, vp[2], &nbytes))
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (argc > 0 && !ToInt32(cx, args[0], &nbytes))
         return false;
 
     if (nbytes < 0) {
         /*
          * We're just not going to support arrays that are bigger than what will fit
          * as an integer value; if someone actually ever complains (validly), then we
          * can fix.
          */
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
         return false;
     }
 
     JSObject *bufobj = create(cx, uint32_t(nbytes));
     if (!bufobj)
         return false;
-    vp->setObject(*bufobj);
+    args.rval().setObject(*bufobj);
     return true;
 }
 
 /*
  * Note that some callers are allowed to pass in a NULL cx, so we allocate with
  * the cx if available and fall back to the runtime.  If oldptr is given,
  * it's expected to be a previously-allocated ObjectElements* pointer that we
  * then realloc.
@@ -1824,67 +1825,66 @@ class TypedArrayObjectTemplate : public 
      * new [Type]Array(otherTypedArray)
      * new [Type]Array(JSArray)
      * new [Type]Array(ArrayBuffer, [optional] byteOffset, [optional] length)
      */
     static JSBool
     class_constructor(JSContext *cx, unsigned argc, Value *vp)
     {
         /* N.B. this is a constructor for protoClass, not fastClass! */
-        JSObject *obj = create(cx, argc, JS_ARGV(cx, vp));
+        CallArgs args = CallArgsFromVp(argc, vp);
+        JSObject *obj = create(cx, args);
         if (!obj)
             return false;
-        vp->setObject(*obj);
+        args.rval().setObject(*obj);
         return true;
     }
 
     static JSObject *
-    create(JSContext *cx, unsigned argc, Value *argv)
+    create(JSContext *cx, const CallArgs& args)
     {
-        /* N.B. there may not be an argv[-2]/argv[-1]. */
-
         /* () or (number) */
         uint32_t len = 0;
-        if (argc == 0 || ValueIsLength(argv[0], &len))
+        if (args.length() == 0 || ValueIsLength(args[0], &len))
             return fromLength(cx, len);
 
         /* (not an object) */
-        if (!argv[0].isObject()) {
+        if (!args[0].isObject()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return NULL;
         }
 
-        RootedObject dataObj(cx, &argv[0].toObject());
+        RootedObject dataObj(cx, &args.get(0).toObject());
 
         /*
          * (typedArray)
          * (type[] array)
          *
          * Otherwise create a new typed array and copy elements 0..len-1
          * properties from the object, treating it as some sort of array.
          * Note that offset and length will be ignored
          */
         if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObject>())
             return fromArray(cx, dataObj);
 
         /* (ArrayBuffer, [byteOffset, [length]]) */
         int32_t byteOffset = 0;
         int32_t length = -1;
 
-        if (argc > 1) {
-            if (!ToInt32(cx, argv[1], &byteOffset))
+        if (args.length() > 1) {
+            if (!ToInt32(cx, args[1], &byteOffset))
                 return NULL;
             if (byteOffset < 0) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "1");
                 return NULL;
             }
 
-            if (argc > 2) {
-                if (!ToInt32(cx, argv[2], &length))
+            if (args.length() > 2) {
+                if (!ToInt32(cx, args[2], &length))
                     return NULL;
                 if (length < 0) {
                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                          JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2");
                     return NULL;
                 }
             }
         }
--- a/js/xpconnect/src/XPCInlines.h
+++ b/js/xpconnect/src/XPCInlines.h
@@ -540,17 +540,17 @@ XPCWrappedNative::SweepTearOffs()
     }
 }
 
 /***************************************************************************/
 
 inline JSBool
 xpc_ForcePropertyResolve(JSContext* cx, JSObject* obj, jsid id)
 {
-    jsval prop;
+    JS::RootedValue prop(cx);
 
     if (!JS_LookupPropertyById(cx, obj, id, &prop))
         return false;
     return true;
 }
 
 inline jsid
 GetRTIdByIndex(JSContext *cx, unsigned index)
--- a/js/xpconnect/src/qsgen.py
+++ b/js/xpconnect/src/qsgen.py
@@ -492,33 +492,33 @@ def writeArgumentUnboxing(f, i, name, ty
     # optional - bool - True if the parameter is optional.
     # rvdeclared - bool - False if no |nsresult rv| has been declared earlier.
 
     typeName = getBuiltinOrNativeTypeName(type)
 
     isSetter = (i is None)
 
     if isSetter:
-        argPtr = "argv"
+        argPtr = "argv[0].address()"
         argVal = "argv[0]"
     elif optional:
         if typeName == "[jsval]":
-            val = "JSVAL_VOID"
+            val = "JS::UndefinedHandleValue"
         else:
-            val = "JSVAL_NULL"
+            val = "JS::NullHandleValue"
         argVal = "(%d < argc ? argv[%d] : %s)" % (i, i, val)
         if typeName == "[jsval]":
             # This should use the rooted argument,
             # however we probably won't ever need to support that.
             argPtr = None
         else:
-            argPtr = "(%d < argc ? &argv[%d] : NULL)" % (i, i)
+            argPtr = "(%d < argc ? argv[%d].address() : NULL)" % (i, i)
     else:
         argVal = "argv[%d]" % i
-        argPtr = "&" + argVal
+        argPtr = argVal + ".address()"
 
     params = {
         'name': name,
         'argVal': argVal,
         'argPtr': argPtr,
         'nullBehavior': nullBehavior or 'DefaultNullBehavior',
         'undefinedBehavior': undefinedBehavior or 'DefaultUndefinedBehavior'
         }
@@ -874,17 +874,17 @@ def writeQuickStub(f, customMethodCalls,
         f.write("    if (argc < %d)\n" % requiredArgs)
         f.write("        return xpc_qsThrow(cx, "
                 "NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n")
 
     # Convert in-parameters.
     rvdeclared = False
     if isMethod:
         if len(member.params) > 0:
-            f.write("    jsval *argv = JS_ARGV(cx, vp);\n")
+            f.write("    JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);\n")
         for i, param in enumerate(member.params):
             argName = 'arg%d' % i
             argTypeKey = argName + 'Type'
             if customMethodCall is None or not argTypeKey in customMethodCall:
                 validateParam(member, param)
                 realtype = param.realtype
             else:
                 realtype = xpidl.Forward(name=customMethodCall[argTypeKey],
@@ -892,17 +892,17 @@ def writeQuickStub(f, customMethodCalls,
             # Emit code to convert this argument from jsval.
             rvdeclared = writeArgumentUnboxing(
                 f, i, argName, realtype,
                 optional=param.optional,
                 rvdeclared=rvdeclared,
                 nullBehavior=param.null,
                 undefinedBehavior=param.undefined)
     elif isSetter:
-        f.write("    jsval *argv = JS_ARGV(cx, vp);\n")
+        f.write("    JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);\n")
         rvdeclared = writeArgumentUnboxing(f, None, 'arg0', member.realtype,
                                            optional=False,
                                            rvdeclared=rvdeclared,
                                            nullBehavior=member.null,
                                            undefinedBehavior=member.undefined,
                                            propIndex=stringtable.stringIndex(member.name))
 
     canFail = customMethodCall is None or customMethodCall.get('canFail', True)
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -322,17 +322,17 @@ ExposedPropertiesOnly::check(JSContext *
     if (!found) {
         return false;
     }
 
     if (id == JSID_VOID)
         return true;
 
     RootedValue exposedProps(cx);
-    if (!JS_LookupPropertyById(cx, wrappedObject, exposedPropsId, exposedProps.address()))
+    if (!JS_LookupPropertyById(cx, wrappedObject, exposedPropsId, &exposedProps))
         return false;
 
     if (exposedProps.isNullOrUndefined())
         return false;
 
     if (!exposedProps.isObject()) {
         EnterAndThrow(cx, wrapper, "__exposedProps__ must be undefined, null, or an Object");
         return false;
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1624,17 +1624,17 @@ XrayWrapper<Base, Traits>::delete_(JSCon
 
     // Check the expando object.
     RootedObject target(cx, Traits::getTargetObject(wrapper));
     RootedObject expando(cx, Traits::singleton.getExpandoObject(cx, target, wrapper));
     JSBool b = true;
     if (expando) {
         JSAutoCompartment ac(cx, expando);
         RootedValue v(cx);
-        if (!JS_DeletePropertyById2(cx, expando, id, v.address()) ||
+        if (!JS_DeletePropertyById2(cx, expando, id, &v) ||
             !JS_ValueToBoolean(cx, v, &b))
         {
             return false;
         }
     }
     *bp = !!b;
     return true;
 }
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -45,16 +45,17 @@ include $(topsrcdir)/build/automation-bu
 		$(topsrcdir)/testing/mozbase/mozdevice/mozdevice/droid.py \
 		$(topsrcdir)/testing/mozbase/mozdevice/mozdevice/Zeroconf.py \
 		$(topsrcdir)/build/automationutils.py \
 		$(topsrcdir)/build/mobile/remoteautomation.py \
 		$(topsrcdir)/build/mobile/b2gautomation.py \
 		gen_template.pl \
 		server.js \
 		chunkifyTests.js \
+		manifestLibrary.js \
 		harness.xul \
 		browser-test-overlay.xul \
 		browser-test.js \
 		cc-analyzer.js \
 		chrome-harness.js \
 		browser-harness.xul \
 		redirect.html \
 		$(topsrcdir)/build/pgo/server-locations.txt \
@@ -198,14 +199,15 @@ endif
 stage-package: stage-chromejar
 endif
 
 $(_DEST_DIR):
 	$(NSINSTALL) -D $@
 
 stage-package:
 	$(NSINSTALL) -D $(PKG_STAGE)/mochitest && $(NSINSTALL) -D $(PKG_STAGE)/bin/plugins && $(NSINSTALL) -D $(DIST)/plugins
+	cp $(DEPTH)/mozinfo.json $(PKG_STAGE)/mochitest
 	(cd $(DEPTH)/_tests/testing/mochitest/ && tar $(TAR_CREATE_FLAGS) - *) | (cd $(PKG_STAGE)/mochitest && tar -xf -)
 	@cp $(DEPTH)/mozinfo.json $(PKG_STAGE)/mochitest
 	@(cd $(DIST_BIN) && tar $(TAR_CREATE_FLAGS) - $(TEST_HARNESS_BINS)) | (cd $(PKG_STAGE)/bin && tar -xf -)
 	@(cd $(DIST_BIN)/components && tar $(TAR_CREATE_FLAGS) - $(TEST_HARNESS_COMPONENTS)) | (cd $(PKG_STAGE)/bin/components && tar -xf -)
 	(cd $(topsrcdir)/build/pgo/certs && tar $(TAR_CREATE_FLAGS) - *) | (cd $(PKG_STAGE)/certs && tar -xf -)
 	@(cd $(DIST)/plugins && tar $(TAR_CREATE_FLAGS) - $(TEST_HARNESS_PLUGINS)) | (cd $(PKG_STAGE)/bin/plugins && tar -xf -)
--- a/testing/mochitest/browser-harness.xul
+++ b/testing/mochitest/browser-harness.xul
@@ -7,16 +7,17 @@
 <window id="browserTestHarness"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="TestStart();"
         title="Browser chrome tests"
         width="1024">
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/MozillaLogger.js"/>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/LogController.js"/>
   <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"/>
+  <script type="application/javascript" src="chrome://mochikit/content/manifestLibrary.js" />
   <script type="application/javascript" src="chrome://mochikit/content/chunkifyTests.js"/>
   <style xmlns="http://www.w3.org/1999/xhtml"><![CDATA[
     #results {
       margin: 5px;
       background-color: window;
       -moz-user-select: text;
     }
 
@@ -162,20 +163,26 @@
           html += "<p class=\"info\">TEST-END | " + path + " | finished in " +
                   this.duration + " ms</p>";
         }
         return html;
       }
     };
 
     // Returns an array of browserTest objects for all the selected tests
-    function listTests() {
-      var links = getTestList();
-      if (!links)
-        return [];
+    function runTests() {
+      gConfig.baseurl = "chrome://mochitests/content";
+      getTestList(gConfig, loadTestList);
+    }
+
+    function loadTestList(links) {
+      if (!links) {
+        createTester({});
+        return;
+      }
 
       // load server.js in so we can share template functions
       var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                            getService(Ci.mozIJSSubScriptLoader);
       var srvScope = {};
       scriptLoader.loadSubScript('chrome://mochikit/content/server.js',
                                  srvScope);
 
@@ -183,38 +190,38 @@
       var fileNameRegexp = /browser_.+\.js$/;
       srvScope.arrayOfTestFiles(links, fileNames, fileNameRegexp);
 
       if (gConfig.totalChunks && gConfig.thisChunk) {
         fileNames = chunkifyTests(fileNames, gConfig.totalChunks,
                                   gConfig.thisChunk, gConfig.chunkByDir);
       }
 
-      return fileNames.map(function (f) new browserTest(f));
+      createTester(fileNames.map(function (f) { return new browserTest(f); }));
     }
 
     function setStatus(aStatusString) {
       document.getElementById("status").value = aStatusString;
     }
 
-    function runTests() {
+    function createTester(links) {
       var windowMediator = Cc['@mozilla.org/appshell/window-mediator;1'].
                              getService(Ci.nsIWindowMediator);
       var winType = gConfig.testRoot == "browser" ? "navigator:browser" :
                     gConfig.testRoot == "metro" ? "navigator:browser" :
                     gConfig.testRoot == "webapprtChrome" ? "webapprt:webapp" :
                     null;
       if (!winType) {
         throw new Error("Unrecognized gConfig.testRoot: " + gConfig.testRoot);
       }
       var testWin = windowMediator.getMostRecentWindow(winType);
 
       setStatus("Running...");
       testWin.focus();
-      var Tester = new testWin.Tester(listTests(), gDumper, testsFinished);
+      var Tester = new testWin.Tester(links, gDumper, testsFinished);
       Tester.start();
     }
 
     function sum(a, b) {
       return a + b;
     }
 
     function getHTMLLogFromTests(aTests) {
--- a/testing/mochitest/chrome-harness.js
+++ b/testing/mochitest/chrome-harness.js
@@ -160,38 +160,42 @@ function zList(base, zReader, baseJarNam
  */
 function getFileListing(basePath, testPath, dir, srvScope)
 {
   var uri = getResolvedURI(basePath);
   var chromeDir = getChromeDir(uri);
   chromeDir.appendRelativePath(dir);
   basePath += '/' + dir;
 
+  if (testPath == "false" || testPath == false) {
+    testPath = "";
+  }
+
   var ioSvc = Components.classes["@mozilla.org/network/io-service;1"].
               getService(Components.interfaces.nsIIOService);
   var testsDirURI = ioSvc.newFileURI(chromeDir);
   var testsDir = ioSvc.newURI(testPath, null, testsDirURI)
                   .QueryInterface(Components.interfaces.nsIFileURL).file;
 
   if (testPath != undefined) {
     var extraPath = testPath;
     
     var fileNameRegexp = /(browser|test)_.+\.(xul|html|js)$/;
 
     // Invalid testPath...
     if (!testsDir.exists())
       return null;
 
     if (testsDir.isFile()) {
-      if (fileNameRegexp.test(testsDir.leafName))
+      if (fileNameRegexp.test(testsDir.leafName)) {
         var singlePath = basePath + '/' + testPath;
         var links = {};
         links[singlePath] = true;
         return links;
-
+      }
       // We were passed a file that's not a test...
       return null;
     }
 
     // otherwise, we were passed a directory of tests
     basePath += "/" + testPath;
   }
   var [links, count] = srvScope.list(basePath, testsDir, true);
@@ -317,72 +321,84 @@ function buildRelativePath(jarentryname,
 
   for (var i = baseParts.length; i < parts.length; i++) {
     targetFile.append(parts[i]);
   }
 
   return targetFile;
 }
 
-function readConfig() {
+function readConfig(filename) {
+  filename = filename || "testConfig.js";
+
   var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"].
                     getService(Components.interfaces.nsIProperties);
   var configFile = fileLocator.get("ProfD", Components.interfaces.nsIFile);
-  configFile.append("testConfig.js");
+  configFile.append(filename);
 
   if (!configFile.exists())
     return {};
 
   var fileInStream = Components.classes["@mozilla.org/network/file-input-stream;1"].
                      createInstance(Components.interfaces.nsIFileInputStream);
   fileInStream.init(configFile, -1, 0, 0);
 
   var str = NetUtil.readInputStreamToString(fileInStream, fileInStream.available());
   fileInStream.close();
   return JSON.parse(str);
 }
 
-function getTestList() {
-  var params = {};
+function registerTests() {
+  var testsURI = Components.classes["@mozilla.org/file/directory_service;1"].
+                 getService(Components.interfaces.nsIProperties).
+                 get("ProfD", Components.interfaces.nsILocalFile);
+  testsURI.append("tests.manifest");
+  var ioSvc = Components.classes["@mozilla.org/network/io-service;1"].
+              getService(Components.interfaces.nsIIOService);
+  var manifestFile = ioSvc.newFileURI(testsURI).
+                     QueryInterface(Components.interfaces.nsIFileURL).file;
+
+  Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar).
+                     autoRegister(manifestFile);
+}
+
+function getTestList(params, callback) {
+  registerTests();
+
+  var baseurl = 'chrome://mochitests/content';
   if (window.parseQueryString) {
     params = parseQueryString(location.search.substring(1), true);
   }
+  if (!params.baseurl) {
+    params.baseurl = baseurl;
+  }
 
   var config = readConfig();
   for (p in params) {
     if (params[p] == 1) {
       config[p] = true;
     } else if (params[p] == 0) {
       config[p] = false;
     } else {
       config[p] = params[p];
     }
   }
   params = config;
+  if (params.manifestFile) {
+    getTestManifest("http://mochi.test:8888/" + params.manifestFile, params, callback);
+    return;
+  }
 
-  var baseurl = 'chrome://mochitests/content';
-  var testsURI = Components.classes["@mozilla.org/file/directory_service;1"]
-                      .getService(Components.interfaces.nsIProperties)
-                      .get("ProfD", Components.interfaces.nsILocalFile);
-  testsURI.append("tests.manifest");
-  var ioSvc = Components.classes["@mozilla.org/network/io-service;1"].
-              getService(Components.interfaces.nsIIOService);
-  var manifestFile = ioSvc.newFileURI(testsURI)
-                  .QueryInterface(Components.interfaces.nsIFileURL).file;
-
-  Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar).
-    autoRegister(manifestFile);
-
+  var links = {};
   // load server.js in so we can share template functions
   var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                        getService(Ci.mozIJSSubScriptLoader);
   var srvScope = {};
   scriptLoader.loadSubScript('chrome://mochikit/content/server.js',
                              srvScope);
-  var links;
 
   if (getResolvedURI(baseurl).JARFile) {
     links = getMochitestJarListing(baseurl, params.testPath, params.testRoot);
   } else {
     links = getFileListing(baseurl, params.testPath, params.testRoot, srvScope);
   }
-  return links;
+  callback(links);
 }
--- a/testing/mochitest/harness.xul
+++ b/testing/mochitest/harness.xul
@@ -16,30 +16,34 @@
   <script type="text/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/ChromePowers.js"/>
   <script type="text/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/TestRunner.js"/>
   <script type="text/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/MozillaLogger.js"/>
   <script type="application/javascript"
           src="chrome://mochikit/content/chrome-harness.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/manifestLibrary.js" />
   <script type="text/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/setup.js" />
   <script type="application/javascript;version=1.7"><![CDATA[
 
 if (Cc === undefined) {
   var Cc = Components.classes;
   var Ci = Components.interfaces;
 }
 
 function loadTests()
 {
   window.removeEventListener("load", loadTests, false);
-  links = getTestList();
+  getTestList({}, linkAndHookup);
+}
  
+function linkAndHookup(links) {
   // load server.js in so we can share template functions
   var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                        getService(Ci.mozIJSSubScriptLoader);
   var srvScope = {};
   scriptLoader.loadSubScript('chrome://mochikit/content/server.js',
                              srvScope);
 
   // generate our test list
--- a/testing/mochitest/jar.mn
+++ b/testing/mochitest/jar.mn
@@ -4,16 +4,17 @@ mochikit.jar:
   content/browser-test.js (browser-test.js)
   content/browser-test-overlay.xul (browser-test-overlay.xul)
   content/cc-analyzer.js (cc-analyzer.js)
   content/chrome-harness.js (chrome-harness.js)
   content/harness.xul (harness.xul)
   content/redirect.html (redirect.html)
   content/server.js (server.js)
   content/chunkifyTests.js (chunkifyTests.js)
+  content/manifestLibrary.js (manifestLibrary.js)
   content/dynamic/getMyDirectory.sjs (dynamic/getMyDirectory.sjs)
   content/static/harness.css (static/harness.css)
   content/tests/SimpleTest/ChromePowers.js (tests/SimpleTest/ChromePowers.js)
   content/tests/SimpleTest/EventUtils.js (tests/SimpleTest/EventUtils.js)
   content/tests/SimpleTest/ChromeUtils.js (tests/SimpleTest/ChromeUtils.js)
   content/tests/SimpleTest/LogController.js (tests/SimpleTest/LogController.js)
   content/tests/SimpleTest/MozillaLogger.js (../specialpowers/content/MozillaLogger.js)
   content/tests/SimpleTest/SpecialPowersObserverAPI.js (../specialpowers/content/SpecialPowersObserverAPI.js)
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/manifestLibrary.js
@@ -0,0 +1,139 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+function parseTestManifest(testManifest, params, callback) {
+  var links = {};
+  var paths = [];
+
+  // Support --test-manifest format for mobile/b2g
+  if ("runtests" in testManifest || "excludetests" in testManifest) {
+    callback(testManifest);
+    return;
+  }
+
+  // For mochitest-chrome and mochitest-browser-chrome harnesses, we 
+  // define tests as links[testname] = true.
+  // For mochitest-plain, we define lists as an array of testnames.
+  for (var obj of testManifest['tests']) {
+    var path = obj['path'];
+    if (params.testRoot != 'tests' && params.testRoot !== undefined) {
+      links[params.baseurl + '/' + params.testRoot + '/' + path] = true
+    } else {
+      paths.push(params.testPrefix + path);
+    }
+  }
+  if (paths.length > 0) {
+    callback(paths);
+  } else {
+    callback(links);
+  }
+}
+
+function getTestManifest(url, params, callback) {
+  var req = new XMLHttpRequest();
+  req.open("GET", url);
+  req.onload = function() {
+    if (req.readyState == 4) {
+      if (req.status == 200) {
+        parseTestManifest(JSON.parse(req.responseText), params, callback);
+      } else {
+        dump("TEST-ERROR: setup.js | error loading " + url + "\n");
+        callback({});
+      }
+    }
+  }
+  req.send();
+}
+
+// Test Filtering Code
+
+/*
+ Open the file referenced by runOnly|exclude and use that to compare against
+ testList
+ parameters:
+   filter = json object of runtests | excludetests
+   testList = array of test names to run
+   runOnly = use runtests vs excludetests in case both are defined
+ returns:
+   filtered version of testList
+*/
+function filterTests(filter, testList, runOnly) {
+
+  var filteredTests = [];
+  var removedTests = [];
+  var runtests = {};
+  var excludetests = {};
+
+  if (filter == null) {
+    return testList;
+  }
+
+  if ('runtests' in filter) {
+    runtests = filter.runtests;
+  }
+  if ('excludetests' in filter) {
+    excludetests = filter.excludetests;
+  }
+  if (!('runtests' in filter) && !('excludetests' in filter)) {
+    if (runOnly == 'true') {
+      runtests = filter;
+    } else {
+      excludetests = filter;
+    }
+  }
+
+  var testRoot = config.testRoot || "tests";
+  // Start with testList, and put everything that's in 'runtests' in
+  // filteredTests.
+  if (Object.keys(runtests).length) {
+    for (var i = 0; i < testList.length; i++) {
+      var testpath = testList[i];
+      var tmppath = testpath.replace(/^\//, '');
+      for (var f in runtests) {
+        // Remove leading /tests/ if exists
+        file = f.replace(/^\//, '')
+        file = file.replace(/^tests\//, '')
+
+        // Match directory or filename, testList has <testroot>/<path>
+        if (tmppath.match(testRoot + "/" + file) != null) {
+          filteredTests.push(testpath);
+          break;
+        }
+      }
+    }
+  } else {
+    filteredTests = testList.slice(0);
+  }
+
+  // Continue with filteredTests, and deselect everything that's in
+  // excludedtests.
+  if (!Object.keys(excludetests).length) {
+    return filteredTests;
+  }
+
+  var refilteredTests = [];
+  for (var i = 0; i < filteredTests.length; i++) {
+    var found = false;
+    var testpath = filteredTests[i];
+    var tmppath = testpath.replace(/^\//, '');
+    for (var f in excludetests) {
+      // Remove leading /tests/ if exists
+      file = f.replace(/^\//, '')
+      file = file.replace(/^tests\//, '')
+
+      // Match directory or filename, testList has <testroot>/<path>
+      if (tmppath.match(testRoot + "/" + file) != null) {
+        found = true;
+        break;
+      }
+    }
+    if (!found) {
+      refilteredTests.push(testpath);
+    }
+  }
+  return refilteredTests;
+}
+
--- a/testing/mochitest/mochitest_options.py
+++ b/testing/mochitest/mochitest_options.py
@@ -265,31 +265,31 @@ class MochitestOptions(optparse.OptionPa
                 "Only available when running a single test. Default cap is 30 runs, "
                 "which can be overwritten with the --repeat parameter.",
           "default": False,
         }],
         [["--run-only-tests"],
         { "action": "store",
           "type": "string",
           "dest": "runOnlyTests",
-          "help": "JSON list of tests that we only want to run, cannot be specified with --exclude-tests. [DEPRECATED- please use --test-manifest]",
-          "default": None,
-        }],
-        [["--exclude-tests"],
-        { "action": "store",
-          "type": "string",
-          "dest": "excludeTests",
-          "help": "JSON list of tests that we want to not run, cannot be specified with --run-only-tests. [DEPRECATED- please use --test-manifest]",
+          "help": "JSON list of tests that we only want to run. [DEPRECATED- please use --test-manifest]",
           "default": None,
         }],
         [["--test-manifest"],
         { "action": "store",
           "type": "string",
           "dest": "testManifest",
-          "help": "JSON list of tests to specify 'runtests' and 'excludetests'.",
+          "help": "JSON list of tests to specify 'runtests'. Old format for mobile specific tests",
+          "default": None,
+        }],
+        [["--manifest"],
+        { "action": "store",
+          "type": "string",
+          "dest": "manifestFile",
+          "help": ".ini format of tests to run.",
           "default": None,
         }],
         [["--failure-file"],
         { "action": "store",
           "type": "string",
           "dest": "failureFile",
           "help": "Filename of the output file where we can store a .json list of failures to be run in the future with --run-only-tests.",
           "default": None,
@@ -316,16 +316,23 @@ class MochitestOptions(optparse.OptionPa
         [["--setpref"],
         { "action": "append",
           "type": "string",
           "default": [],
           "dest": "extraPrefs",
           "metavar": "PREF=VALUE",
           "help": "defines an extra user preference",
         }],
+        [["--build-info-json"],
+        { "action": "store",
+          "type": "string",
+          "default": None,
+          "dest": "mozInfo",
+          "help": "path to mozinfo.json to determine build time options",
+        }],
     ]
 
     def __init__(self, automation=None, **kwargs):
         self._automation = automation or Automation()
         optparse.OptionParser.__init__(self, **kwargs)
         defaults = {}
 
         # we want to pass down everything from self._automation.__all__
@@ -395,33 +402,28 @@ class MochitestOptions(optparse.OptionPa
             if not self._automation.IS_WIN32:
                 self.error("use-vmware-recording is only supported on Windows.")
             mochitest.vmwareHelperPath = os.path.join(
                 options.utilityPath, VMWARE_RECORDING_HELPER_BASENAME + ".dll")
             if not os.path.exists(mochitest.vmwareHelperPath):
                 self.error("%s not found, cannot automate VMware recording." %
                            mochitest.vmwareHelperPath)
 
-        if options.runOnlyTests != None and options.excludeTests != None:
-            self.error("We can only support --run-only-tests OR --exclude-tests, not both.  Please consider using --test-manifest instead.")
-
-        if options.testManifest != None and (options.runOnlyTests != None or options.excludeTests != None):
-            self.error("Please use --test-manifest only and not --run-only-tests or --exclude-tests.")
+        if options.testManifest and options.runOnlyTests:
+            self.error("Please use --test-manifest only and not --run-only-tests")
 
         if options.runOnlyTests:
             if not os.path.exists(os.path.abspath(options.runOnlyTests)):
-                self.error("unable to find --run-only-tests file '%s'" % options.runOnlyTests);
+                self.error("unable to find --run-only-tests file '%s'" % options.runOnlyTests)
+            options.runOnly = True
             options.testManifest = options.runOnlyTests
-            options.runOnly = True
+            options.runOnlyTests = None
 
-        if options.excludeTests:
-            if not os.path.exists(os.path.abspath(options.excludeTests)):
-                self.error("unable to find --exclude-tests file '%s'" % options.excludeTests);
-            options.testManifest = options.excludeTests
-            options.runOnly = False
+        if options.manifestFile and options.testManifest:
+            self.error("Unable to support both --manifest and --test-manifest/--run-only-tests at the same time")
 
         if options.webapprtContent and options.webapprtChrome:
             self.error("Only one of --webapprt-content and --webapprt-chrome may be given.")
 
         # Try to guess the testing modules directory.
         # This somewhat grotesque hack allows the buildbot machines to find the
         # modules directory without having to configure the buildbot hosts. This
         # code should never be executed in local runs because the build system
@@ -458,16 +460,26 @@ class MochitestOptions(optparse.OptionPa
                 self.error("%s not found, cannot launch immersive tests." %
                            mochitest.immersiveHelperPath)
 
         if options.runUntilFailure:
             if not os.path.isfile(os.path.join(mochitest.oldcwd, os.path.dirname(__file__), mochitest.getTestRoot(options), options.testPath)):
                 self.error("--run-until-failure can only be used together with --test-path specifying a single test.")
             if not options.repeat:
                 options.repeat = 29
+
+        if not options.mozInfo:
+            if build_obj:
+                options.mozInfo = os.path.join(build_obj.get_binary_path(), 'mozinfo.json')
+            else:
+                options.mozInfo = os.path.abspath('mozinfo.json')
+
+        if not os.path.isfile(options.mozInfo):
+            self.error("Unable to file build information file (mozinfo.json) at this location: %s" % options.mozInfo)
+
         return options
 
 
 class B2GOptions(MochitestOptions):
     b2g_options = [
         [["--b2gpath"],
         { "action": "store",
           "type": "string",
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -18,18 +18,20 @@ SCRIPT_DIR = os.path.abspath(os.path.rea
 sys.path.insert(0, SCRIPT_DIR);
 
 import shutil
 from urllib import quote_plus as encodeURIComponent
 import urllib2
 from automation import Automation
 from automationutils import getDebuggerInfo, isURL, processLeakLog
 from mochitest_options import MochitestOptions
+from manifestparser import TestManifest
+import mozinfo
+import json
 
-import mozinfo
 import mozlog
 
 log = mozlog.getLogger('Mochitest')
 
 
 #######################
 # HTTP SERVER SUPPORT #
 #######################
@@ -234,23 +236,47 @@ class MochitestUtilsMixin(object):
       if os.path.isfile(os.path.join(self.oldcwd, os.path.dirname(__file__), self.TEST_PATH, options.testPath)) and options.repeat > 0:
         self.urlOpts.append("testname=%s" % ("/").join([self.TEST_PATH, options.testPath]))
       if options.testManifest:
         self.urlOpts.append("testManifest=%s" % options.testManifest)
         if hasattr(options, 'runOnly') and options.runOnly:
           self.urlOpts.append("runOnly=true")
         else:
           self.urlOpts.append("runOnly=false")
+      if options.manifestFile:
+        self.urlOpts.append("manifestFile=%s" % options.manifestFile)
       if options.failureFile:
         self.urlOpts.append("failureFile=%s" % self.getFullPath(options.failureFile))
       if options.runSlower:
         self.urlOpts.append("runSlower=true")
 
   def buildTestPath(self, options):
-    """ Build the url path to the specific test harness and test file or directory """
+    """ Build the url path to the specific test harness and test file or directory
+        Build a manifest of tests to run and write out a json file for the harness to read
+    """
+    if options.manifestFile and os.path.isfile(options.manifestFile):
+      manifest = TestManifest(strict=False)
+      manifest.read(options.manifestFile)
+      # Bug 883858 - return all tests including disabled tests 
+      tests = manifest.active_tests(disabled=False, **mozinfo.info)
+      paths = []
+      for test in tests:
+        tp = test['path'].split(self.getTestRoot(options), 1)[1].strip('/')
+
+        # Filter out tests if we are using --test-path
+        if options.testPath and not tp.startswith(options.testPath):
+          continue
+
+        paths.append({'path': tp})
+
+      # Bug 883865 - add this functionality into manifestDestiny
+      with open('tests.json', 'w') as manifestFile:
+        manifestFile.write(json.dumps({'tests': paths}))
+      options.manifestFile = 'tests.json'
+
     testHost = "http://mochi.test:8888"
     testURL = ("/").join([testHost, self.TEST_PATH, options.testPath])
     if os.path.isfile(os.path.join(self.oldcwd, os.path.dirname(__file__), self.TEST_PATH, options.testPath)) and options.repeat > 0:
        testURL = ("/").join([testHost, self.PLAIN_LOOP_PATH])
     if options.chrome or options.a11y:
        testURL = ("/").join([testHost, self.CHROME_PATH])
     elif options.browserChrome:
       testURL = "about:blank"
--- a/testing/mochitest/server.js
+++ b/testing/mochitest/server.js
@@ -573,19 +573,23 @@ function regularListing(metadata, respon
 }
 
 /**
  * Produce a test harness page containing all the test cases
  * below it, recursively.
  */
 function testListing(metadata, response)
 {
-  var [links, count] = list(metadata.path,
-                            metadata.getProperty("directory"),
-                            true);
+  var links = {};
+  var count = 0;
+  if (metadata.queryString.indexOf('manifestFile') == -1) {
+    [links, count] = list(metadata.path,
+                          metadata.getProperty("directory"),
+                          true);
+  }
   var table_class = metadata.queryString.indexOf("hideResultsTable=1") > -1 ? "invisible": "";
 
   let testname = (metadata.queryString.indexOf("testname=") > -1)
                  ? metadata.queryString.match(/testname=([^&]+)/)[1]
                  : "";
 
   dumpn("count: " + count);
   var tests = testname
@@ -602,16 +606,18 @@ function testListing(metadata, response)
                  src: "/tests/SimpleTest/LogController.js"}),
         SCRIPT({type: "text/javascript",
                  src: "/tests/SimpleTest/TestRunner.js"}),
         SCRIPT({type: "text/javascript",
                  src: "/tests/SimpleTest/MozillaLogger.js"}),
         SCRIPT({type: "text/javascript",
                  src: "/chunkifyTests.js"}),
         SCRIPT({type: "text/javascript",
+                 src: "/manifestLibrary.js"}),
+        SCRIPT({type: "text/javascript",
                  src: "/tests/SimpleTest/setup.js"}),
         SCRIPT({type: "text/javascript"},
                "window.onload =  hookup; gTestList=" + tests + ";"
         )
       ),
       BODY(
         DIV({class: "container"},
           H2("--> ", A({href: "#", id: "runtests"}, "Run Tests"), " <--"),
--- a/testing/mochitest/tests/SimpleTest/setup.js
+++ b/testing/mochitest/tests/SimpleTest/setup.js
@@ -60,16 +60,29 @@ if (config.testRoot == "chrome" || confi
       config[p] = true;
     } else if (params[p] == 0) {
       config[p] = false;
     } else {
       config[p] = params[p];
     }
   }
   params = config;
+  params.baseurl = "chrome://mochitests/content";
+} else {
+  params.baseurl = "";
+}
+
+if (params.testRoot == "browser") {
+  params.testPrefix = "chrome://mochitests/content/browser/";
+} else if (params.testRoot == "chrome") {
+  params.testPrefix = "chrome://mochitests/content/chrome/";
+} else if (params.testRoot == "a11y") {
+  params.testPrefix = "chrome://mochitests/content/a11y/";
+} else {
+  params.testPrefix = "/tests/";
 }
 
 // set the per-test timeout if specified in the query string
 if (params.timeout) {
   TestRunner.timeout = parseInt(params.timeout) * 1000;
 }
 
 // log levels for console and logfile
@@ -108,24 +121,29 @@ if (!params.quiet) {
   TestRunner.logger.addListener("dumpListener", consoleLevel + "", dumpListener);
 }
 
 // A temporary hack for android 4.0 where Fennec utilizes the pandaboard so much it reboots
 if (params.runSlower) {
   TestRunner.runSlower = true;
 }
 
-
 var gTestList = [];
 var RunSet = {}
 RunSet.runall = function(e) {
   // Filter tests to include|exclude tests based on data in params.filter.
   // This allows for including or excluding tests from the gTestList
-  gTestList = filterTests(params.testManifest, params.runOnly);
+  if (params.testManifest) {
+    getTestManifest("http://mochi.test:8888/" + params.testManifest, params, function(filter) { gTestList = filterTests(filter, gTestList, params.runOnly); RunSet.runtests(); });
+  } else {
+    RunSet.runtests();
+  }
+}
 
+RunSet.runtests = function(e) {
   // Which tests we're going to run
   var my_tests = gTestList;
 
   if (params.totalChunks && params.thisChunk) {
     my_tests = chunkifyTests(my_tests, params.totalChunks, params.thisChunk, params.chunkByDir, TestRunner.logger);
   }
 
   if (params.shuffle) {
@@ -148,106 +166,16 @@ RunSet.reloadAndRunAll = function(e) {
     window.location.href = window.location.href;
   } else if (window.location.search) {
     window.location.href += "&autorun=1";
   } else {
     window.location.href += "?autorun=1";
   }  
 };
 
-// Test Filtering Code
-
-// Open the file referenced by runOnly|exclude and use that to compare against
-// gTestList.  Return a modified version of gTestList
-function filterTests(filterFile, runOnly) {
-  var filteredTests = [];
-  var removedTests = [];
-  var runtests = {};
-  var excludetests = {};
-
-  if (filterFile == null) {
-    return gTestList;
-  }
-
-  var datafile = "http://mochi.test:8888/" + filterFile;
-  var objXml = new XMLHttpRequest();
-  objXml.open("GET",datafile,false);
-  objXml.send(null);
-  try {
-    var filter = JSON.parse(objXml.responseText);
-  } catch (ex) {
-    dump("INFO | setup.js | error loading or parsing '" + datafile + "'\n");
-    return gTestList;
-  }
-
-  if ('runtests' in filter) {
-    runtests = filter.runtests;
-  }
-  if ('excludetests' in filter)
-    excludetests = filter.excludetests;
-  if (!('runtests' in filter) && !('excludetests' in filter)) {
-    if (runOnly == 'true') {
-      runtests = filter;
-    } else
-      excludetests = filter;
-  }
-
-  var testRoot = config.testRoot || "tests";
-  // Start with gTestList, and put everything that's in 'runtests' in
-  // filteredTests.
-  if (Object.keys(runtests).length) {
-    for (var i = 0; i < gTestList.length; i++) {
-      var test_path = gTestList[i];
-      var tmp_path = test_path.replace(/^\//, '');
-      for (var f in runtests) {
-        // Remove leading /tests/ if exists
-        file = f.replace(/^\//, '')
-        file = file.replace(/^tests\//, '')
-
-        // Match directory or filename, gTestList has <testroot>/<path>
-        if (tmp_path.match(testRoot + "/" + file) != null) {
-          filteredTests.push(test_path);
-          break;
-        }
-      }
-    }
-  }
-  else {
-    filteredTests = gTestList.slice(0);
-  }
-
-  // Continue with filteredTests, and deselect everything that's in
-  // excludedtests.
-  if (Object.keys(excludetests).length) {
-    var refilteredTests = [];
-    for (var i = 0; i < filteredTests.length; i++) {
-      var found = false;
-      var test_path = filteredTests[i];
-      var tmp_path = test_path.replace(/^\//, '');
-      for (var f in excludetests) {
-        // Remove leading /tests/ if exists
-        file = f.replace(/^\//, '')
-        file = file.replace(/^tests\//, '')
-
-        // Match directory or filename, gTestList has <testroot>/<path>
-        if (tmp_path.match(testRoot + "/" + file) != null) {
-          found = true;
-          break;
-        }
-      }
-      if (!found) {
-        refilteredTests.push(test_path);
-      }
-    }
-    filteredTests = refilteredTests;
-  }
-
-  return filteredTests;
-}
-
 // UI Stuff
 function toggleVisible(elem) {
     toggleElementClass("invisible", elem);
 }
 
 function makeVisible(elem) {
     removeElementClass(elem, "invisible");
 }
@@ -272,15 +200,32 @@ function toggleNonTests (e) {
     $("toggleNonTests").innerHTML = "Hide Non-Tests";
   } else {
     $("toggleNonTests").innerHTML = "Show Non-Tests";
   }
 }
 
 // hook up our buttons
 function hookup() {
+  if (params.manifestFile) {
+    getTestManifest("http://mochi.test:8888/" + params.manifestFile, params, hookupTests);
+  } else {
+    hookupTests(gTestList);
+  }
+}
+
+function hookupTests(testList) {
+  if (testList.length > 0) {
+    gTestList = testList;
+  } else {
+    gTestList = [];
+    for (var obj in testList) {
+        gTestList.push(obj);
+    }
+  }
+
   document.getElementById('runtests').onclick = RunSet.reloadAndRunAll;
   document.getElementById('toggleNonTests').onclick = toggleNonTests; 
   // run automatically if autorun specified
   if (params.autorun) {
     RunSet.runall();
   }
 }
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -39,25 +39,27 @@ RUN_MOCHITEST_B2G_DESKTOP = \
 
 RUN_MOCHITEST = \
   rm -f ./$@.log && \
   $(PYTHON) _tests/testing/mochitest/runtests.py --autorun --close-when-done \
     --console-level=INFO --log-file=./$@.log --file-level=INFO \
     --failure-file=$(call core_abspath,_tests/testing/mochitest/makefailures.json) \
     --testing-modules-dir=$(call core_abspath,_tests/modules) \
     --extra-profile-file=$(DIST)/plugins \
+    --build-info-json=$(call core_abspath,$(DEPTH)/mozinfo.json) \
     $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
 
 RERUN_MOCHITEST = \
   rm -f ./$@.log && \
   $(PYTHON) _tests/testing/mochitest/runtests.py --autorun --close-when-done \
     --console-level=INFO --log-file=./$@.log --file-level=INFO \
     --run-only-tests=makefailures.json \
     --testing-modules-dir=$(call core_abspath,_tests/modules) \
     --extra-profile-file=$(DIST)/plugins \
+    --build-info-json=$(call core_abspath,$(DEPTH)/mozinfo.json) \
     $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
 
 RUN_MOCHITEST_REMOTE = \
   rm -f ./$@.log && \
   $(PYTHON) _tests/testing/mochitest/runtestsremote.py --autorun --close-when-done \
     --console-level=INFO --log-file=./$@.log --file-level=INFO $(DM_FLAGS) --dm_trans=$(DM_TRANS) \
     --app=$(TEST_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \
     --testing-modules-dir=$(call core_abspath,_tests/modules) --httpd-path=. \