Bug 1534608. MOZ_CAN_RUN_SCRIPT should disallow non-stack refptr arguments. r=emilio
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 13 Mar 2019 00:30:11 +0000
changeset 521626 19f3dfdf76da0c37c018683808add3d19f192e56
parent 521625 bb3918322c3c0f903e9a11065e56b921fe11bff0
child 521627 ca44b5f0bb412281fc034e5d3102c1a2ff44afee
push id10867
push userdvarga@mozilla.com
push dateThu, 14 Mar 2019 15:20:45 +0000
treeherdermozilla-beta@abad13547875 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1534608
milestone67.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
Bug 1534608. MOZ_CAN_RUN_SCRIPT should disallow non-stack refptr arguments. r=emilio Differential Revision: https://phabricator.services.mozilla.com/D23217
build/clang-plugin/CanRunScriptChecker.cpp
build/clang-plugin/tests/TestCanRunScript.cpp
dom/base/nsDocumentEncoder.cpp
dom/html/HTMLImageElement.cpp
dom/html/HTMLImageElement.h
dom/html/HTMLInputElement.cpp
dom/ipc/TabChild.cpp
gfx/layers/apz/util/ChromeProcessController.cpp
layout/base/PresShell.cpp
layout/base/gtest/TestAccessibleCaretEventHub.cpp
layout/generic/nsFrameSelection.cpp
--- a/build/clang-plugin/CanRunScriptChecker.cpp
+++ b/build/clang-plugin/CanRunScriptChecker.cpp
@@ -2,43 +2,62 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CanRunScriptChecker.h"
 #include "CustomMatchers.h"
 
 void CanRunScriptChecker::registerMatchers(MatchFinder *AstMatcher) {
   auto Refcounted = qualType(hasDeclaration(cxxRecordDecl(isRefCounted())));
+  auto StackSmartPtr =
+    ignoreTrivials(
+      declRefExpr(to(varDecl(hasAutomaticStorageDuration())),
+                  hasType(isSmartPtrToRefCounted())));
+  auto MozKnownLiveCall =
+    callExpr(callee(functionDecl(hasName("MOZ_KnownLive"))));
+
   auto InvalidArg =
       // We want to find any expression,
       ignoreTrivials(expr(
           // which has a refcounted pointer type,
           anyOf(
             hasType(Refcounted),
             hasType(pointsTo(Refcounted)),
-            hasType(references(Refcounted))
+            hasType(references(Refcounted)),
+            hasType(isSmartPtrToRefCounted())
           ),
           // and which is not this,
           unless(cxxThisExpr()),
-          // and which is not a method call on a smart ptr,
-          unless(cxxMemberCallExpr(on(hasType(isSmartPtrToRefCounted())))),
-          // and which is not calling operator* on a smart ptr.
+          // and which is not a stack smart ptr
+          unless(StackSmartPtr),
+          // and which is not a method call on a stack smart ptr,
+          unless(cxxMemberCallExpr(on(StackSmartPtr))),
+          // and which is not calling operator* on a stack smart ptr.
           unless(
             allOf(
               cxxOperatorCallExpr(hasOverloadedOperatorName("*")),
               callExpr(allOf(
-                hasAnyArgument(hasType(isSmartPtrToRefCounted())),
+                hasAnyArgument(StackSmartPtr),
                 argumentCountIs(1)
               ))
             )
           ),
           // and which is not a parameter of the parent function,
           unless(declRefExpr(to(parmVarDecl()))),
           // and which is not a MOZ_KnownLive wrapped value.
-          unless(callExpr(callee(functionDecl(hasName("MOZ_KnownLive"))))),
+          unless(
+            anyOf(
+              MozKnownLiveCall,
+              // MOZ_KnownLive applied to a RefPtr or nsCOMPtr just returns that
+              // same RefPtr/nsCOMPtr type which causes us to have a conversion
+              // operator applied after the MOZ_KnownLive.
+              cxxMemberCallExpr(on(allOf(hasType(isSmartPtrToRefCounted()),
+                                         MozKnownLiveCall)))
+            )
+          ),
           expr().bind("invalidArg")));
 
   auto OptionalInvalidExplicitArg = anyOf(
       // We want to find any argument which is invalid.
       hasAnyArgument(InvalidArg),
 
       // This makes this matcher optional.
       anything());
--- a/build/clang-plugin/tests/TestCanRunScript.cpp
+++ b/build/clang-plugin/tests/TestCanRunScript.cpp
@@ -191,12 +191,43 @@ MOZ_CAN_RUN_SCRIPT void test_ref_8() {
 MOZ_CAN_RUN_SCRIPT void test_maybe() {
   // FIXME(emilio): This should generate an error, but it's pre-existing!
   mozilla::Maybe<RefCountedBase*> unsafe;
   unsafe.emplace(new RefCountedBase);
   (*unsafe)->method_test();
 }
 
 MOZ_CAN_RUN_SCRIPT void test_maybe_2() {
+  // FIXME(bz): This should not generate an error!
   mozilla::Maybe<RefPtr<RefCountedBase>> safe;
   safe.emplace(new RefCountedBase);
-  (*safe)->method_test();
+  (*safe)->method_test(); // expected-error {{arguments must all be strong refs or parent parameters when calling a function marked as MOZ_CAN_RUN_SCRIPT (including the implicit object argument)}}
 }
+
+struct DisallowMemberArgs {
+  RefPtr<RefCountedBase> mRefCounted;
+  MOZ_CAN_RUN_SCRIPT void foo() {
+    mRefCounted->method_test(); // expected-error {{arguments must all be strong refs or parent parameters when calling a function marked as MOZ_CAN_RUN_SCRIPT (including the implicit object argument)}}
+  }
+  MOZ_CAN_RUN_SCRIPT void bar() {
+    test2(mRefCounted); // expected-error {{arguments must all be strong refs or parent parameters when calling a function marked as MOZ_CAN_RUN_SCRIPT (including the implicit object argument)}}
+  }
+};
+
+struct DisallowMemberArgsWithGet {
+  RefPtr<RefCountedBase> mRefCounted;
+  MOZ_CAN_RUN_SCRIPT void foo() {
+    mRefCounted.get()->method_test(); // expected-error {{arguments must all be strong refs or parent parameters when calling a function marked as MOZ_CAN_RUN_SCRIPT (including the implicit object argument)}}
+  }
+  MOZ_CAN_RUN_SCRIPT void bar() {
+    test2(mRefCounted.get()); // expected-error {{arguments must all be strong refs or parent parameters when calling a function marked as MOZ_CAN_RUN_SCRIPT (including the implicit object argument)}}
+  }
+};
+
+struct AllowKnownLiveMemberArgs {
+  RefPtr<RefCountedBase> mRefCounted;
+  MOZ_CAN_RUN_SCRIPT void foo() {
+    MOZ_KnownLive(mRefCounted)->method_test();
+  }
+  MOZ_CAN_RUN_SCRIPT void bar() {
+    test2(MOZ_KnownLive(mRefCounted));
+  }
+};
--- a/dom/base/nsDocumentEncoder.cpp
+++ b/dom/base/nsDocumentEncoder.cpp
@@ -1202,17 +1202,19 @@ nsHTMLCopyEncoder::SetSelection(Selectio
     MOZ_ASSERT(myRange);
 
     // adjust range to include any ancestors who's children are entirely
     // selected
     nsresult rv = PromoteRange(myRange);
     NS_ENSURE_SUCCESS(rv, rv);
 
     ErrorResult result;
-    mSelection->AddRangeInternal(*myRange, mDocument, result);
+    RefPtr<Selection> selection(mSelection);
+    RefPtr<Document> document(mDocument);
+    selection->AddRangeInternal(*myRange, document, result);
     rv = result.StealNSResult();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -652,16 +652,26 @@ already_AddRefed<HTMLImageElement> HTMLI
         return nullptr;
       }
     }
   }
 
   return img.forget();
 }
 
+uint32_t HTMLImageElement::Height() {
+  RefPtr<imgRequestProxy> currentRequest(mCurrentRequest);
+  return GetWidthHeightForImage(currentRequest).height;
+}
+
+uint32_t HTMLImageElement::Width() {
+  RefPtr<imgRequestProxy> currentRequest(mCurrentRequest);
+  return GetWidthHeightForImage(currentRequest).width;
+}
+
 uint32_t HTMLImageElement::NaturalHeight() {
   uint32_t height = nsImageLoadingContent::NaturalHeight();
 
   if (mResponsiveSelector) {
     double density = mResponsiveSelector->GetSelectedImageDensity();
     MOZ_ASSERT(density >= 0.0);
     height = NSToIntRound(double(height) / density);
   }
--- a/dom/html/HTMLImageElement.h
+++ b/dom/html/HTMLImageElement.h
@@ -85,25 +85,21 @@ class HTMLImageElement final : public ns
   nsresult CopyInnerTo(HTMLImageElement* aDest);
 
   void MaybeLoadImage(bool aAlwaysForceLoad);
 
   bool IsMap() { return GetBoolAttr(nsGkAtoms::ismap); }
   void SetIsMap(bool aIsMap, ErrorResult& aError) {
     SetHTMLBoolAttr(nsGkAtoms::ismap, aIsMap, aError);
   }
-  MOZ_CAN_RUN_SCRIPT uint32_t Width() {
-    return GetWidthHeightForImage(mCurrentRequest).width;
-  }
+  MOZ_CAN_RUN_SCRIPT uint32_t Width();
   void SetWidth(uint32_t aWidth, ErrorResult& aError) {
     SetUnsignedIntAttr(nsGkAtoms::width, aWidth, 0, aError);
   }
-  MOZ_CAN_RUN_SCRIPT uint32_t Height() {
-    return GetWidthHeightForImage(mCurrentRequest).height;
-  }
+  MOZ_CAN_RUN_SCRIPT uint32_t Height();
   void SetHeight(uint32_t aHeight, ErrorResult& aError) {
     SetUnsignedIntAttr(nsGkAtoms::height, aHeight, 0, aError);
   }
   uint32_t NaturalWidth();
   uint32_t NaturalHeight();
   bool Complete();
   uint32_t Hspace() { return GetUnsignedIntAttr(nsGkAtoms::hspace, 0); }
   void SetHspace(uint32_t aHspace, ErrorResult& aError) {
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -244,17 +244,18 @@ class DispatchChangeEventCallback final 
     }
 
     mInputElement->SetFilesOrDirectories(array, true);
     Unused << NS_WARN_IF(NS_FAILED(DispatchEvents()));
   }
 
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   nsresult DispatchEvents() {
-    nsresult rv = nsContentUtils::DispatchInputEvent(mInputElement);
+    RefPtr<HTMLInputElement> inputElement(mInputElement);
+    nsresult rv = nsContentUtils::DispatchInputEvent(inputElement);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch input event");
 
     rv = nsContentUtils::DispatchTrustedEvent(
         mInputElement->OwnerDoc(), static_cast<Element*>(mInputElement.get()),
         NS_LITERAL_STRING("change"), CanBubble::eYes, Cancelable::eNo);
 
     return rv;
   }
@@ -600,17 +601,18 @@ nsresult nsColorPickerShownCallback::Upd
     }
   }
 
   if (!valueChanged) {
     return NS_OK;
   }
 
   mValueChanged = true;
-  DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(mInput);
+  RefPtr<HTMLInputElement> input(mInput);
+  DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(input);
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                        "Failed to dispatch input event");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsColorPickerShownCallback::Update(const nsAString& aColor) {
   return UpdateInternal(aColor, true);
@@ -1376,17 +1378,18 @@ void HTMLInputElement::GetType(nsAString
 }
 
 int32_t HTMLInputElement::TabIndexDefault() { return 0; }
 
 uint32_t HTMLInputElement::Height() {
   if (mType != NS_FORM_INPUT_IMAGE) {
     return 0;
   }
-  return GetWidthHeightForImage(mCurrentRequest).height;
+  RefPtr<imgRequestProxy> currentRequest(mCurrentRequest);
+  return GetWidthHeightForImage(currentRequest).height;
 }
 
 void HTMLInputElement::SetIndeterminateInternal(bool aValue,
                                                 bool aShouldInvalidate) {
   mIndeterminate = aValue;
 
   if (aShouldInvalidate) {
     // Repaint the frame
@@ -1400,17 +1403,18 @@ void HTMLInputElement::SetIndeterminateI
 void HTMLInputElement::SetIndeterminate(bool aValue) {
   SetIndeterminateInternal(aValue, true);
 }
 
 uint32_t HTMLInputElement::Width() {
   if (mType != NS_FORM_INPUT_IMAGE) {
     return 0;
   }
-  return GetWidthHeightForImage(mCurrentRequest).width;
+  RefPtr<imgRequestProxy> currentRequest(mCurrentRequest);
+  return GetWidthHeightForImage(currentRequest).width;
 }
 
 void HTMLInputElement::GetValue(nsAString& aValue, CallerType aCallerType) {
   GetValueInternal(aValue, aCallerType);
 
   // Don't return non-sanitized value for types that are experimental on mobile
   // or datetime types
   if (IsExperimentalMobileType(mType) || IsDateTimeInputType(mType)) {
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1273,23 +1273,25 @@ mozilla::ipc::IPCResult TabChild::RecvHa
       break;
     case GeckoContentController::TapType::eSecondTap:
       if (mTabChildMessageManager) {
         mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 2);
       }
       break;
     case GeckoContentController::TapType::eLongTap:
       if (mTabChildMessageManager) {
-        mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers,
-                                       aGuid, aInputBlockId);
+        RefPtr<APZEventState> eventState(mAPZEventState);
+        eventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid,
+                                   aInputBlockId);
       }
       break;
     case GeckoContentController::TapType::eLongTapUp:
       if (mTabChildMessageManager) {
-        mAPZEventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
+        RefPtr<APZEventState> eventState(mAPZEventState);
+        eventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
       }
       break;
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult TabChild::RecvNormalPriorityHandleTap(
     const GeckoContentController::TapType& aType,
@@ -1385,18 +1387,19 @@ mozilla::ipc::IPCResult TabChild::RecvSt
 mozilla::ipc::IPCResult TabChild::RecvMouseEvent(
     const nsString& aType, const float& aX, const float& aY,
     const int32_t& aButton, const int32_t& aClickCount,
     const int32_t& aModifiers, const bool& aIgnoreRootScrollFrame) {
   // IPDL doesn't hold a strong reference to protocols as they're not required
   // to be refcounted. This function can run script, which may trigger a nested
   // event loop, which may release this, so we hold a strong reference here.
   RefPtr<TabChild> kungFuDeathGrip(this);
+  nsCOMPtr<nsIPresShell> presShell(GetPresShell());
   APZCCallbackHelper::DispatchMouseEvent(
-      GetPresShell(), aType, CSSPoint(aX, aY), aButton, aClickCount, aModifiers,
+      presShell, aType, CSSPoint(aX, aY), aButton, aClickCount, aModifiers,
       aIgnoreRootScrollFrame, MouseEvent_Binding::MOZ_SOURCE_UNKNOWN,
       0 /* Use the default value here. */);
   return IPC_OK();
 }
 
 void TabChild::ProcessPendingCoalescedMouseDataAndDispatchEvents() {
   if (!mCoalesceMouseMoveEvents || !mCoalescedMouseEventFlusher) {
     // We don't enable mouse coalescing or we are destroying TabChild.
--- a/gfx/layers/apz/util/ChromeProcessController.cpp
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -194,23 +194,27 @@ void ChromeProcessController::HandleTap(
       mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 1);
       break;
     case TapType::eDoubleTap:
       HandleDoubleTap(point, aModifiers, aGuid);
       break;
     case TapType::eSecondTap:
       mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 2);
       break;
-    case TapType::eLongTap:
-      mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid,
-                                     aInputBlockId);
+    case TapType::eLongTap: {
+      RefPtr<APZEventState> eventState(mAPZEventState);
+      eventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid,
+                                 aInputBlockId);
       break;
-    case TapType::eLongTapUp:
-      mAPZEventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
+    }
+    case TapType::eLongTapUp: {
+      RefPtr<APZEventState> eventState(mAPZEventState);
+      eventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
       break;
+    }
   }
 }
 
 void ChromeProcessController::NotifyPinchGesture(
     PinchGestureInput::PinchGestureType aType, const ScrollableLayerGuid& aGuid,
     LayoutDeviceCoord aSpanChange, Modifiers aModifiers) {
   if (MessageLoop::current() != mUILoop) {
     mUILoop->PostTask(
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -6704,18 +6704,21 @@ nsresult PresShell::EventHandler::Handle
 
   // Handle the event in the correct shell.
   // We pass the subshell's root frame as the frame to start from. This is
   // the only correct alternative; if the event was captured then it
   // must have been captured by us or some ancestor shell and we
   // now ask the subshell to dispatch it normally.
   EventHandler eventHandler(*eventTargetData.mPresShell);
   AutoCurrentEventInfoSetter eventInfoSetter(eventHandler, eventTargetData);
+  // eventTargetData is on the stack and is guaranteed to keep its
+  // mOverrideClickTarget alive, so we can just use MOZ_KnownLive here.
   nsresult rv = eventHandler.HandleEventWithCurrentEventInfo(
-      aGUIEvent, aEventStatus, true, eventTargetData.mOverrideClickTarget);
+      aGUIEvent, aEventStatus, true,
+      MOZ_KnownLive(eventTargetData.mOverrideClickTarget));
 #ifdef DEBUG
   eventTargetData.mPresShell->ShowEventTargetDebug();
 #endif
   return rv;
 }
 
 bool PresShell::EventHandler::MaybeFlushPendingNotifications(
     WidgetGUIEvent* aGUIEvent) {
@@ -6854,21 +6857,22 @@ bool PresShell::EventHandler::DispatchPr
     }
 
     targetFrame = aPointerCapturingContent->GetPrimaryFrame();
     aEventTargetData->mFrame = targetFrame;
   }
 
   AutoWeakFrame weakTargetFrame(targetFrame);
   AutoWeakFrame weakFrame(aEventTargetData->mFrame);
+  nsCOMPtr<nsIContent> content(aEventTargetData->mContent);
+  RefPtr<PresShell> presShell(aEventTargetData->mPresShell);
   nsCOMPtr<nsIContent> targetContent;
   PointerEventHandler::DispatchPointerFromMouseOrTouch(
-      aEventTargetData->mPresShell, aEventTargetData->mFrame,
-      aEventTargetData->mContent, aGUIEvent, aDontRetargetEvents, aEventStatus,
-      getter_AddRefs(targetContent));
+      presShell, aEventTargetData->mFrame, content, aGUIEvent,
+      aDontRetargetEvents, aEventStatus, getter_AddRefs(targetContent));
 
   // If the target frame is alive, the caller should keep handling the event
   // unless event target frame is destroyed.
   if (weakTargetFrame.IsAlive()) {
     return weakFrame.IsAlive();
   }
 
   // If the event is not a mouse event, the caller should keep handling the
--- a/layout/base/gtest/TestAccessibleCaretEventHub.cpp
+++ b/layout/base/gtest/TestAccessibleCaretEventHub.cpp
@@ -170,18 +170,19 @@ class AccessibleCaretEventHubTester : pu
 
   MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestAsyncPanZoomScroll();
 
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void HandleEventAndCheckState(
       UniquePtr<WidgetEvent> aEvent,
       MockAccessibleCaretEventHub::State* aExpectedState,
       nsEventStatus aExpectedEventStatus) {
-    nsEventStatus rv = mHub->HandleEvent(aEvent.get());
-    EXPECT_EQ(mHub->GetState(), aExpectedState);
+    RefPtr<MockAccessibleCaretEventHub> hub(mHub);
+    nsEventStatus rv = hub->HandleEvent(aEvent.get());
+    EXPECT_EQ(hub->GetState(), aExpectedState);
     EXPECT_EQ(rv, aExpectedEventStatus);
   }
 
   void CheckState(MockAccessibleCaretEventHub::State* aExpectedState) {
     EXPECT_EQ(mHub->GetState(), aExpectedState);
   }
 
   template <typename PressEventCreator, typename ReleaseEventCreator>
@@ -532,24 +533,25 @@ void AccessibleCaretEventHubTester::Test
   HandleEventAndCheckState(aPressEventCreator(1, 1),
                            MockAccessibleCaretEventHub::PressNoCaretState(),
                            nsEventStatus_eIgnore);
 
   HandleEventAndCheckState(CreateLongTapEvent(1, 1),
                            MockAccessibleCaretEventHub::LongTapState(),
                            nsEventStatus_eIgnore);
 
-  mHub->AsyncPanZoomStarted();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+  RefPtr<MockAccessibleCaretEventHub> hub(mHub);
+  hub->AsyncPanZoomStarted();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
-  mHub->ScrollPositionChanged();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+  hub->ScrollPositionChanged();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
-  mHub->AsyncPanZoomStopped();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+  hub->AsyncPanZoomStopped();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
 
   HandleEventAndCheckState(aReleaseEventCreator(1, 1),
                            MockAccessibleCaretEventHub::NoActionState(),
                            nsEventStatus_eIgnore);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestMouseLongTapWithSelectWordFailed)
 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
@@ -635,29 +637,30 @@ void AccessibleCaretEventHubTester::Test
 
   HandleEventAndCheckState(aMoveEventCreator(100, 100),
                            MockAccessibleCaretEventHub::PressNoCaretState(),
                            nsEventStatus_eIgnore);
 
   check.Call("1");
 
   // Event driven scroll started
-  mHub->AsyncPanZoomStarted();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+  RefPtr<MockAccessibleCaretEventHub> hub(mHub);
+  hub->AsyncPanZoomStarted();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
   HandleEventAndCheckState(aMoveEventCreator(160, 160),
                            MockAccessibleCaretEventHub::ScrollState(),
                            nsEventStatus_eIgnore);
 
-  mHub->ScrollPositionChanged();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+  hub->ScrollPositionChanged();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
   // Event driven scroll ended
-  mHub->AsyncPanZoomStopped();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+  hub->AsyncPanZoomStopped();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
 
   HandleEventAndCheckState(aReleaseEventCreator(210, 210),
                            MockAccessibleCaretEventHub::NoActionState(),
                            nsEventStatus_eIgnore);
 
   check.Call("2");
 
   // Receive another press event.
@@ -667,25 +670,25 @@ void AccessibleCaretEventHubTester::Test
 
   HandleEventAndCheckState(aMoveEventCreator(230, 230),
                            MockAccessibleCaretEventHub::PressNoCaretState(),
                            nsEventStatus_eIgnore);
 
   check.Call("3");
 
   // Another APZ scroll started
-  mHub->AsyncPanZoomStarted();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+  hub->AsyncPanZoomStarted();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
-  mHub->ScrollPositionChanged();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+  hub->ScrollPositionChanged();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
   // Another APZ scroll ended
-  mHub->AsyncPanZoomStopped();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+  hub->AsyncPanZoomStopped();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
 
   HandleEventAndCheckState(aReleaseEventCreator(310, 310),
                            MockAccessibleCaretEventHub::NoActionState(),
                            nsEventStatus_eIgnore);
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScroll)
 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { TestAsyncPanZoomScroll(); }
@@ -706,74 +709,77 @@ void AccessibleCaretEventHubTester::Test
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(),
                 OnScrollPositionChanged());
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd());
   }
 
   // First APZ scrolling.
   check.Call("1");
 
-  mHub->AsyncPanZoomStarted();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+  RefPtr<MockAccessibleCaretEventHub> hub(mHub);
+  hub->AsyncPanZoomStarted();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
-  mHub->ScrollPositionChanged();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+  hub->ScrollPositionChanged();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
-  mHub->AsyncPanZoomStopped();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+  hub->AsyncPanZoomStopped();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
 
   // Second APZ scrolling.
   check.Call("2");
 
-  mHub->AsyncPanZoomStarted();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+  hub->AsyncPanZoomStarted();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
-  mHub->ScrollPositionChanged();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+  hub->ScrollPositionChanged();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
-  mHub->AsyncPanZoomStopped();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+  hub->AsyncPanZoomStopped();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScrollStartedThenBlur)
 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
   {
     InSequence dummy;
 
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart());
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd()).Times(0);
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnBlur());
   }
 
-  mHub->AsyncPanZoomStarted();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+  RefPtr<MockAccessibleCaretEventHub> hub(mHub);
+  hub->AsyncPanZoomStarted();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
-  mHub->ScrollPositionChanged();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+  hub->ScrollPositionChanged();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
-  mHub->NotifyBlur(true);
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+  hub->NotifyBlur(true);
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
 }
 
 TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScrollEndedThenBlur)
 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
   {
     InSequence dummy;
 
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart());
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd());
     EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnBlur());
   }
 
-  mHub->AsyncPanZoomStarted();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
-
-  mHub->ScrollPositionChanged();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+  RefPtr<MockAccessibleCaretEventHub> hub(mHub);
+  hub->AsyncPanZoomStarted();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
-  mHub->AsyncPanZoomStopped();
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+  hub->ScrollPositionChanged();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
 
-  mHub->NotifyBlur(true);
-  EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+  hub->AsyncPanZoomStopped();
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+
+  hub->NotifyBlur(true);
+  EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
 }
 
 }  // namespace mozilla
--- a/layout/generic/nsFrameSelection.cpp
+++ b/layout/generic/nsFrameSelection.cpp
@@ -806,17 +806,19 @@ nsresult nsFrameSelection::MoveCaret(nsD
               eSelectLine == aAmount) {
             SetCaretBidiLevel(theFrame->GetEmbeddingLevel());
           } else {
             BidiLevelFromMove(mShell, pos.mResultContent, pos.mContentOffset,
                               aAmount, tHint);
           }
       }
     }
-    result = TakeFocus(pos.mResultContent, pos.mContentOffset,
+    // "pos" is on the stack, so pos.mResultContent has stack lifetime, so using
+    // MOZ_KnownLive is ok.
+    result = TakeFocus(MOZ_KnownLive(pos.mResultContent), pos.mContentOffset,
                        pos.mContentOffset, tHint, aContinueSelection, false);
   } else if (aAmount <= eSelectWordNoSpace && aDirection == eDirNext &&
              !aContinueSelection) {
     // Collapse selection if PeekOffset failed, we either
     //  1. bumped into the BRFrame, bug 207623
     //  2. had select-all in a text input (DIV range), bug 352759.
     bool isBRFrame = frame->IsBrFrame();
     sel->Collapse(sel->GetFocusNode(), sel->FocusOffset());
@@ -2644,17 +2646,18 @@ void nsFrameSelection::SetAncestorLimite
     mAncestorLimiter = aLimiter;
     int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
     if (!mDomSelections[index]) return;
 
     if (!IsValidSelectionPoint(this, mDomSelections[index]->GetFocusNode())) {
       ClearNormalSelection();
       if (mAncestorLimiter) {
         PostReason(nsISelectionListener::NO_REASON);
-        TakeFocus(mAncestorLimiter, 0, 0, CARET_ASSOCIATE_BEFORE, false, false);
+        nsCOMPtr<nsIContent> limiter(mAncestorLimiter);
+        TakeFocus(limiter, 0, 0, CARET_ASSOCIATE_BEFORE, false, false);
       }
     }
   }
 }
 
 nsresult nsFrameSelection::DeleteFromDocument() {
   // If we're already collapsed, then we do nothing (bug 719503).
   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);