diff options
Diffstat (limited to 'layout/base')
-rw-r--r-- | layout/base/RestyleManagerBase.cpp | 63 | ||||
-rw-r--r-- | layout/base/nsCSSFrameConstructor.cpp | 19 | ||||
-rw-r--r-- | layout/base/nsCaret.cpp | 4 | ||||
-rw-r--r-- | layout/base/nsChangeHint.h | 55 | ||||
-rw-r--r-- | layout/base/nsDocumentViewer.cpp | 7 | ||||
-rw-r--r-- | layout/base/nsIDocumentViewerPrint.h | 2 | ||||
-rw-r--r-- | layout/base/nsIPresShell.h | 14 | ||||
-rw-r--r-- | layout/base/nsPresContext.cpp | 9 | ||||
-rw-r--r-- | layout/base/nsPresContext.h | 22 | ||||
-rw-r--r-- | layout/base/nsPresShell.cpp | 144 | ||||
-rw-r--r-- | layout/base/tests/bug1078327_inner.html | 8 | ||||
-rw-r--r-- | layout/base/tests/bug1162990_inner_1.html | 4 | ||||
-rw-r--r-- | layout/base/tests/bug1162990_inner_2.html | 2 | ||||
-rw-r--r-- | layout/base/tests/bug976963_inner.html | 241 | ||||
-rw-r--r-- | layout/base/tests/bug977003_inner_5.html | 8 | ||||
-rw-r--r-- | layout/base/tests/bug977003_inner_6.html | 6 | ||||
-rw-r--r-- | layout/base/tests/mochitest.ini | 2 | ||||
-rw-r--r-- | layout/base/tests/test_bug976963.html | 35 |
18 files changed, 262 insertions, 383 deletions
diff --git a/layout/base/RestyleManagerBase.cpp b/layout/base/RestyleManagerBase.cpp index 9a5ce43eb..d96d9dbbb 100644 --- a/layout/base/RestyleManagerBase.cpp +++ b/layout/base/RestyleManagerBase.cpp @@ -154,7 +154,7 @@ RestyleManagerBase::ChangeHintToString(nsChangeHint aHint) "NeutralChange", "InvalidateRenderingObservers", "ReflowChangesSizeOrPosition", "UpdateComputedBSize", "UpdateUsesOpacity", "UpdateBackgroundPosition", - "AddOrRemoveTransform" + "AddOrRemoveTransform", "CSSOverflowChange", }; static_assert(nsChangeHint_AllHints == (1 << ArrayLength(names)) - 1, "Name list doesn't match change hints."); @@ -1070,6 +1070,67 @@ RestyleManagerBase::ProcessRestyledFrames(nsStyleChangeList& aChangeList) FramePropertyTable* propTable = presContext->PropertyTable(); nsCSSFrameConstructor* frameConstructor = presContext->FrameConstructor(); + // Handle nsChangeHint_CSSOverflowChange, by either updating the + // scrollbars on the viewport, or upgrading the change hint to frame-reconstruct. + for (nsStyleChangeData& data : aChangeList) { + if (data.mHint & nsChangeHint_CSSOverflowChange) { + data.mHint &= ~nsChangeHint_CSSOverflowChange; + bool doReconstruct = true; // assume the worst + + // Only bother with this if we're html/body, since: + // (a) It'd be *expensive* to reframe these particular nodes. They're + // at the root, so reframing would mean rebuilding the world. + // (b) It's often *unnecessary* to reframe for "overflow" changes on + // these particular nodes. In general, the only reason we reframe + // for "overflow" changes is so we can construct (or destroy) a + // scrollframe & scrollbars -- and the html/body nodes often don't + // need their own scrollframe/scrollbars because they coopt the ones + // on the viewport (which always exist). So depending on whether + // that's happening, we can skip the reframe for these nodes. + if (data.mContent->IsAnyOfHTMLElements(nsGkAtoms::body, + nsGkAtoms::html)) { + // If the restyled element provided/provides the scrollbar styles for + // the viewport before and/or after this restyle, AND it's not coopting + // that responsibility from some other element (which would need + // reconstruction to make its own scrollframe now), THEN: we don't need + // to reconstruct - we can just reflow, because no scrollframe is being + // added/removed. + nsIContent* prevOverrideNode = + presContext->GetViewportScrollbarStylesOverrideNode(); + nsIContent* newOverrideNode = + presContext->UpdateViewportScrollbarStylesOverride(); + + if (data.mContent == prevOverrideNode || + data.mContent == newOverrideNode) { + // If we get here, the restyled element provided the scrollbar styles + // for viewport before this restyle, OR it will provide them after. + if (!prevOverrideNode || !newOverrideNode || + prevOverrideNode == newOverrideNode) { + // If we get here, the restyled element is NOT replacing (or being + // replaced by) some other element as the viewport's + // scrollbar-styles provider. (If it were, we'd potentially need to + // reframe to create a dedicated scrollframe for whichever element + // is being booted from providing viewport scrollbar styles.) + // + // Under these conditions, we're OK to assume that this "overflow" + // change only impacts the root viewport's scrollframe, which + // already exists, so we can simply reflow instead of reframing. + // When requesting this reflow, we send the exact same change hints + // that "width" and "height" would send (since conceptually, + // adding/removing scrollbars is like changing the available + // space). + data.mHint |= (nsChangeHint_ReflowHintsForISizeChange | + nsChangeHint_ReflowHintsForBSizeChange); + doReconstruct = false; + } + } + } + if (doReconstruct) { + data.mHint |= nsChangeHint_ReconstructFrame; + } + } + } + // Make sure to not rebuild quote or counter lists while we're // processing restyles frameConstructor->BeginUpdate(); diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index a118c38f9..767298b85 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -3658,13 +3658,13 @@ nsCSSFrameConstructor::FindInputData(Element* aElement, nsCSSAnonBoxes::buttonContent) }, // TODO: this is temporary until a frame is written: bug 635240. SIMPLE_INT_CREATE(NS_FORM_INPUT_NUMBER, NS_NewNumberControlFrame), - // TODO: this is temporary until a frame is written: bug 888320. - SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewTextControlFrame), #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) // On Android/B2G, date/time input appears as a normal text box. SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewTextControlFrame), + SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewTextControlFrame), #else SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewDateTimeControlFrame), + SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewDateTimeControlFrame), #endif // TODO: this is temporary until a frame is written: bug 888320 SIMPLE_INT_CREATE(NS_FORM_INPUT_MONTH, NS_NewTextControlFrame), @@ -8246,11 +8246,19 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, *aDestroyedFramesFor = aChild; } + nsPresContext* presContext = mPresShell->GetPresContext(); + MOZ_ASSERT(presContext, "Our presShell should have a valid presContext"); + if (aChild->IsHTMLElement(nsGkAtoms::body) || (!aContainer && aChild->IsElement())) { - // This might be the element we propagated viewport scrollbar - // styles from. Recompute those. - mPresShell->GetPresContext()->UpdateViewportScrollbarStylesOverride(); + // We might be removing the element that we propagated viewport scrollbar + // styles from. Recompute those. (This clause covers two of the three + // possible scrollbar-propagation sources: the <body> [as aChild or a + // descendant] and the root node. The other possible scrollbar-propagation + // source is a fullscreen element, and we have code elsewhere to update + // scrollbars after fullscreen elements are removed -- specifically, it's + // part of the fullscreen cleanup code called by Element::UnbindFromTree.) + presContext->UpdateViewportScrollbarStylesOverride(); } // XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and @@ -8316,7 +8324,6 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, ClearDisplayContentsIn(aChild, aContainer); } - nsPresContext* presContext = mPresShell->GetPresContext(); #ifdef MOZ_XUL if (NotifyListBoxBody(presContext, aContainer, aChild, aOldNextSibling, childFrame, CONTENT_REMOVED)) { diff --git a/layout/base/nsCaret.cpp b/layout/base/nsCaret.cpp index 2f08d156e..8ad435950 100644 --- a/layout/base/nsCaret.cpp +++ b/layout/base/nsCaret.cpp @@ -200,8 +200,8 @@ nsCaret::ComputeMetrics(nsIFrame* aFrame, int32_t aOffset, nscoord aCaretHeight) // between 0 and 1 goes up to 1 so we don't let the caret disappear. int32_t tpp = aFrame->PresContext()->AppUnitsPerDevPixel(); Metrics result; - result.mCaretWidth = NS_ROUND_BORDER_TO_PIXELS(caretWidth, tpp); - result.mBidiIndicatorSize = NS_ROUND_BORDER_TO_PIXELS(bidiIndicatorSize, tpp); + result.mCaretWidth = NS_ROUND_CARET_TO_PIXELS(caretWidth, tpp); + result.mBidiIndicatorSize = NS_ROUND_CARET_TO_PIXELS(bidiIndicatorSize, tpp); return result; } diff --git a/layout/base/nsChangeHint.h b/layout/base/nsChangeHint.h index 318b84840..eb2709de6 100644 --- a/layout/base/nsChangeHint.h +++ b/layout/base/nsChangeHint.h @@ -217,6 +217,16 @@ enum nsChangeHint { */ nsChangeHint_AddOrRemoveTransform = 1 << 27, + /** + * Indicates that the overflow-x and/or overflow-y property changed. + * + * In most cases, this is equivalent to nsChangeHint_ReconstructFrame. But + * in some special cases where the change is really targeting the viewport's + * scrollframe, this is instead equivalent to nsChangeHint_AllReflowHints + * (because the viewport always has an associated scrollframe). + */ + nsChangeHint_CSSOverflowChange = 1 << 28, + // IMPORTANT NOTE: When adding new hints, consider whether you need // to add them to NS_HintsNotHandledForDescendantsIn() below. Please // also add them to RestyleManager::ChangeHintToString and modify @@ -225,7 +235,7 @@ enum nsChangeHint { /** * Dummy hint value for all hints. It exists for compile time check. */ - nsChangeHint_AllHints = (1 << 28) - 1, + nsChangeHint_AllHints = (1 << 29) - 1, }; // Redefine these operators to return nothing. This will catch any use @@ -306,6 +316,7 @@ inline nsChangeHint operator^=(nsChangeHint& aLeft, nsChangeHint aRight) nsChangeHint_UpdatePostTransformOverflow | \ nsChangeHint_UpdateParentOverflow | \ nsChangeHint_ChildrenOnlyTransform | \ + nsChangeHint_CSSOverflowChange | \ nsChangeHint_RecomputePosition | \ nsChangeHint_UpdateContainingBlock | \ nsChangeHint_AddOrRemoveTransform | \ @@ -374,6 +385,48 @@ inline nsChangeHint NS_HintsNotHandledForDescendantsIn(nsChangeHint aChangeHint) nsChangeHint_ClearAncestorIntrinsics | \ nsChangeHint_ClearDescendantIntrinsics | \ nsChangeHint_NeedDirtyReflow) + +// Below are the change hints that we send for ISize & BSize changes. +// Each is similar to nsChangeHint_AllReflowHints with a few changes. + +// * For an ISize change, we send nsChangeHint_AllReflowHints, with two bits +// excluded: nsChangeHint_ClearDescendantIntrinsics (because an ancestor's +// inline-size change can't affect descendant intrinsic sizes), and +// nsChangeHint_NeedDirtyReflow (because ISize changes don't need to *force* +// all descendants to reflow). +#define nsChangeHint_ReflowHintsForISizeChange \ + nsChangeHint(nsChangeHint_AllReflowHints & \ + ~(nsChangeHint_ClearDescendantIntrinsics | \ + nsChangeHint_NeedDirtyReflow)) + +// * For a BSize change, we send almost the same hints as for ISize changes, +// with one extra: nsChangeHint_UpdateComputedBSize. We need this hint because +// BSize changes CAN affect descendant intrinsic sizes, due to replaced +// elements with percentage BSizes in descendants which also have percentage +// BSizes. nsChangeHint_UpdateComputedBSize clears intrinsic sizes for frames +// that have such replaced elements. (We could instead send +// nsChangeHint_ClearDescendantIntrinsics, but that's broader than we need.) +// +// NOTE: You might think that BSize changes could exclude +// nsChangeHint_ClearAncestorIntrinsics (which is inline-axis specific), but we +// do need to send it, to clear cached results from CSS Flex measuring reflows. +#define nsChangeHint_ReflowHintsForBSizeChange \ + nsChangeHint((nsChangeHint_AllReflowHints | \ + nsChangeHint_UpdateComputedBSize) & \ + ~(nsChangeHint_ClearDescendantIntrinsics | \ + nsChangeHint_NeedDirtyReflow)) + +// * For changes to the float area of an already-floated element, we need all +// reflow hints, but not the ones that apply to descendants. +// Our descendants aren't impacted when our float area only changes +// placement but not size/shape. (e.g. if we change which side we float to). +// But our ancestors/siblings are potentially impacted, so we need to send +// the non-descendant reflow hints. +#define nsChangeHint_ReflowHintsForFloatAreaChange \ + nsChangeHint(nsChangeHint_AllReflowHints & \ + ~(nsChangeHint_ClearDescendantIntrinsics | \ + nsChangeHint_NeedDirtyReflow)) + #define NS_STYLE_HINT_REFLOW \ nsChangeHint(NS_STYLE_HINT_VISUAL | nsChangeHint_AllReflowHints) diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 782ac332c..137efb3cd 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -1617,7 +1617,6 @@ nsDocumentViewer::Destroy() // We also keep the viewer from being cached in session history, since // we require all documents there to be sanitized. if (mDestroyRefCount != 0) { - --mDestroyRefCount; return NS_OK; } @@ -4426,6 +4425,12 @@ nsDocumentViewer::IncrementDestroyRefCount() ++mDestroyRefCount; } +void +nsDocumentViewer::DecrementDestroyRefCount() +{ + --mDestroyRefCount; +} + //------------------------------------------------------------ #if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW) diff --git a/layout/base/nsIDocumentViewerPrint.h b/layout/base/nsIDocumentViewerPrint.h index 8bc2465a1..e3c397f3a 100644 --- a/layout/base/nsIDocumentViewerPrint.h +++ b/layout/base/nsIDocumentViewerPrint.h @@ -41,6 +41,7 @@ public: virtual mozilla::StyleSetHandle CreateStyleSet(nsIDocument* aDocument) = 0; virtual void IncrementDestroyRefCount() = 0; + virtual void DecrementDestroyRefCount() = 0; virtual void ReturnToGalleyPresentation() = 0; @@ -75,6 +76,7 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentViewerPrint, bool GetIsPrintPreview() override; \ mozilla::StyleSetHandle CreateStyleSet(nsIDocument* aDocument) override; \ void IncrementDestroyRefCount() override; \ + void DecrementDestroyRefCount() override; \ void ReturnToGalleyPresentation() override; \ void OnDonePrinting() override; \ bool IsInitializedForPrintPreview() override; \ diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index cbbae0e8f..5990402ed 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -1298,11 +1298,11 @@ public: } }; - static void DispatchGotOrLostPointerCaptureEvent(bool aIsGotCapture, - uint32_t aPointerId, - uint16_t aPointerType, - bool aIsPrimary, - nsIContent* aCaptureTarget); + static void DispatchGotOrLostPointerCaptureEvent( + bool aIsGotCapture, + const mozilla::WidgetPointerEvent* aPointerEvent, + nsIContent* aCaptureTarget); + static PointerCaptureInfo* GetPointerCaptureInfo(uint32_t aPointerId); static void SetPointerCapturingContent(uint32_t aPointerId, nsIContent* aContent); @@ -1311,8 +1311,8 @@ public: // CheckPointerCaptureState checks cases, when got/lostpointercapture events // should be fired. - static void CheckPointerCaptureState(uint32_t aPointerId, - uint16_t aPointerType, bool aIsPrimary); + static void CheckPointerCaptureState( + const mozilla::WidgetPointerEvent* aPointerEvent); // GetPointerInfo returns true if pointer with aPointerId is situated in // device, false otherwise. diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index d9f7b368c..4a54a8432 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -208,6 +208,7 @@ nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType) mTextZoom(1.0), mFullZoom(1.0), mOverrideDPPX(0.0), mLastFontInflationScreenSize(gfxSize(-1.0, -1.0)), mPageSize(-1, -1), mPPScale(1.0f), + mViewportScrollbarOverrideNode(nullptr), mViewportStyleScrollbar(NS_STYLE_OVERFLOW_AUTO, NS_STYLE_OVERFLOW_AUTO), mImageAnimationModePref(imgIContainer::kNormalAnimMode), mAllInvalidated(false), @@ -1423,10 +1424,10 @@ nsPresContext::UpdateViewportScrollbarStylesOverride() // Start off with our default styles, and then update them as needed. mViewportStyleScrollbar = ScrollbarStyles(NS_STYLE_OVERFLOW_AUTO, NS_STYLE_OVERFLOW_AUTO); - nsIContent* propagatedFrom = nullptr; + mViewportScrollbarOverrideNode = nullptr; // Don't propagate the scrollbar state in printing or print preview. if (!IsPaginated()) { - propagatedFrom = + mViewportScrollbarOverrideNode = GetPropagatedScrollbarStylesForViewport(this, &mViewportStyleScrollbar); } @@ -1438,13 +1439,13 @@ nsPresContext::UpdateViewportScrollbarStylesOverride() // the styles are from, so that the state of those elements is not // affected across fullscreen change. if (fullscreenElement != document->GetRootElement() && - fullscreenElement != propagatedFrom) { + fullscreenElement != mViewportScrollbarOverrideNode) { mViewportStyleScrollbar = ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN); } } - return propagatedFrom; + return mViewportScrollbarOverrideNode; } bool diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index 4fdc60a2e..d8f876291 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -719,7 +719,18 @@ public: * it was propagated from. */ nsIContent* UpdateViewportScrollbarStylesOverride(); - const ScrollbarStyles& GetViewportScrollbarStylesOverride() + + /** + * Returns the cached result from the last call to + * UpdateViewportScrollbarStylesOverride() -- i.e. return the node + * whose scrollbar styles we have propagated to the viewport (or nullptr if + * there is no such node). + */ + nsIContent* GetViewportScrollbarStylesOverrideNode() const { + return mViewportScrollbarOverrideNode; + } + + const ScrollbarStyles& GetViewportScrollbarStylesOverride() const { return mViewportStyleScrollbar; } @@ -1310,7 +1321,16 @@ protected: nscolor mBodyTextColor; + // This is a non-owning pointer. May be null. If non-null, it's guaranteed + // to be pointing to a node that's still alive, because we'll reset it in + // UpdateViewportScrollbarStylesOverride() as part of the cleanup code + // when this node is removed from the document. (For <body> and the root node, + // this call happens in nsCSSFrameConstructor::ContentRemoved(). For + // fullscreen elements, it happens in the fullscreen-specific cleanup + // invoked by Element::UnbindFromTree().) + nsIContent* MOZ_NON_OWNING_REF mViewportScrollbarOverrideNode; ScrollbarStyles mViewportStyleScrollbar; + uint8_t mFocusRingWidth; bool mExistThrottledUpdates; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 56ac370b9..42b39c860 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -6514,10 +6514,11 @@ nsIPresShell::GetPointerCapturingContent(uint32_t aPointerId) } /* static */ void -nsIPresShell::CheckPointerCaptureState(uint32_t aPointerId, - uint16_t aPointerType, bool aIsPrimary) +nsIPresShell::CheckPointerCaptureState(const WidgetPointerEvent* aPointerEvent) { - PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aPointerId); + PointerCaptureInfo* captureInfo = + GetPointerCaptureInfo(aPointerEvent->pointerId); + if (captureInfo && captureInfo->mPendingContent != captureInfo->mOverrideContent) { // cache captureInfo->mPendingContent since it may be changed in the pointer @@ -6525,17 +6526,16 @@ nsIPresShell::CheckPointerCaptureState(uint32_t aPointerId, nsIContent* pendingContent = captureInfo->mPendingContent.get(); if (captureInfo->mOverrideContent) { DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, - aPointerId, aPointerType, aIsPrimary, + aPointerEvent, captureInfo->mOverrideContent); } if (pendingContent) { - DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aPointerId, - aPointerType, aIsPrimary, - pendingContent); + DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, + aPointerEvent, pendingContent); } captureInfo->mOverrideContent = pendingContent; if (captureInfo->Empty()) { - sPointerCaptureList->Remove(aPointerId); + sPointerCaptureList->Remove(aPointerEvent->pointerId); } } } @@ -6976,37 +6976,30 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell, return NS_OK; } -class ReleasePointerCaptureCaller +class ReleasePointerCaptureCaller final { public: - ReleasePointerCaptureCaller() : - mPointerId(0), - mPointerType(nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN), - mIsPrimary(false), - mIsSet(false) + ReleasePointerCaptureCaller() + : mPointerEvent(nullptr) { } ~ReleasePointerCaptureCaller() { - if (mIsSet) { - nsIPresShell::ReleasePointerCapturingContent(mPointerId); - nsIPresShell::CheckPointerCaptureState(mPointerId, mPointerType, - mIsPrimary); + if (mPointerEvent) { + nsIPresShell::ReleasePointerCapturingContent(mPointerEvent->pointerId); + nsIPresShell::CheckPointerCaptureState(mPointerEvent); } } - void SetTarget(uint32_t aPointerId, uint16_t aPointerType, bool aIsPrimary) + + void SetTarget(const WidgetPointerEvent* aPointerEvent) { - mPointerId = aPointerId; - mPointerType = aPointerType; - mIsPrimary = aIsPrimary; - mIsSet = true; + MOZ_ASSERT(aPointerEvent); + mPointerEvent = aPointerEvent; } private: - int32_t mPointerId; - uint16_t mPointerType; - bool mIsPrimary; - bool mIsSet; + // This is synchronously used inside PresShell::HandleEvent. + const WidgetPointerEvent* mPointerEvent; }; static bool @@ -7719,9 +7712,7 @@ PresShell::HandleEvent(nsIFrame* aFrame, nsWeakFrame frameKeeper(frame); // Handle pending pointer capture before any pointer events except // gotpointercapture / lostpointercapture. - CheckPointerCaptureState(pointerEvent->pointerId, - pointerEvent->inputSource, - pointerEvent->mIsPrimary); + CheckPointerCaptureState(pointerEvent); // Prevent application crashes, in case damaged frame. if (!frameKeeper.IsAlive()) { frame = nullptr; @@ -7742,33 +7733,29 @@ PresShell::HandleEvent(nsIFrame* aFrame, } } - if (aEvent->mClass == ePointerEventClass && - aEvent->mMessage != ePointerDown) { - if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { - uint32_t pointerId = pointerEvent->pointerId; + // Mouse events should be fired to the same target as their mapped pointer + // events + if ((aEvent->mClass == ePointerEventClass || + aEvent->mClass == eMouseEventClass) && + aEvent->mMessage != ePointerDown && aEvent->mMessage != eMouseDown) { + if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) { + uint32_t pointerId = mouseEvent->pointerId; nsIContent* pointerCapturingContent = GetPointerCapturingContent(pointerId); if (pointerCapturingContent) { if (nsIFrame* capturingFrame = pointerCapturingContent->GetPrimaryFrame()) { - // If pointer capture is set, we should suppress - // pointerover/pointerenter events for all elements except element - // which have pointer capture. (Code in EventStateManager) - pointerEvent->retargetedByPointerCapture = - frame && frame->GetContent() && - !nsContentUtils::ContentIsDescendantOf(frame->GetContent(), - pointerCapturingContent); frame = capturingFrame; } - if (pointerEvent->mMessage == ePointerUp || - pointerEvent->mMessage == ePointerCancel) { + if (aEvent->mMessage == ePointerUp || + aEvent->mMessage == ePointerCancel) { // Implicitly releasing capture for given pointer. // ePointerLostCapture should be send after ePointerUp or // ePointerCancel. - releasePointerCaptureCaller.SetTarget(pointerId, - pointerEvent->inputSource, - pointerEvent->mIsPrimary); + WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent(); + MOZ_ASSERT(pointerEvent); + releasePointerCaptureCaller.SetTarget(pointerEvent); } } } @@ -8204,6 +8191,8 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, } case eMouseDown: case eMouseUp: + case ePointerDown: + case ePointerUp: isHandlingUserInput = true; break; @@ -8340,35 +8329,44 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, return rv; } -void -nsIPresShell::DispatchGotOrLostPointerCaptureEvent(bool aIsGotCapture, - uint32_t aPointerId, - uint16_t aPointerType, - bool aIsPrimary, - nsIContent* aCaptureTarget) -{ - PointerEventInit init; - init.mPointerId = aPointerId; - init.mBubbles = true; - ConvertPointerTypeToString(aPointerType, init.mPointerType); - init.mIsPrimary = aIsPrimary; - RefPtr<mozilla::dom::PointerEvent> event; - event = PointerEvent::Constructor(aCaptureTarget, - aIsGotCapture - ? NS_LITERAL_STRING("gotpointercapture") - : NS_LITERAL_STRING("lostpointercapture"), - init); - if (event) { +/* static */ void +nsIPresShell::DispatchGotOrLostPointerCaptureEvent( + bool aIsGotCapture, + const WidgetPointerEvent* aPointerEvent, + nsIContent* aCaptureTarget) +{ + nsIDocument* targetDoc = aCaptureTarget->OwnerDoc(); + nsCOMPtr<nsIPresShell> shell = targetDoc->GetShell(); + NS_ENSURE_TRUE_VOID(shell); + + if (!aIsGotCapture && !aCaptureTarget->IsInUncomposedDoc()) { + // If the capturing element was removed from the DOM tree, fire + // ePointerLostCapture at the document. + PointerEventInit init; + init.mPointerId = aPointerEvent->pointerId; + init.mBubbles = true; + init.mComposed = true; + ConvertPointerTypeToString(aPointerEvent->inputSource, init.mPointerType); + init.mIsPrimary = aPointerEvent->mIsPrimary; + RefPtr<mozilla::dom::PointerEvent> event; + event = PointerEvent::Constructor(aCaptureTarget, + NS_LITERAL_STRING("lostpointercapture"), + init); bool dummy; - // If the capturing element was removed from the DOM tree, - // lostpointercapture event should be fired at the document. - if (!aIsGotCapture && !aCaptureTarget->IsInUncomposedDoc()) { - aCaptureTarget->OwnerDoc()->DispatchEvent(event->InternalDOMEvent(), - &dummy); - } else { - aCaptureTarget->DispatchEvent(event->InternalDOMEvent(), &dummy); - } + targetDoc->DispatchEvent(event->InternalDOMEvent(), &dummy); + return; } + nsEventStatus status = nsEventStatus_eIgnore; + WidgetPointerEvent localEvent(aPointerEvent->IsTrusted(), + aIsGotCapture ? ePointerGotCapture : + ePointerLostCapture, + aPointerEvent->mWidget); + localEvent.AssignPointerEventData(*aPointerEvent, true); + nsresult rv = shell->HandleEventWithTarget( + &localEvent, + aCaptureTarget->GetPrimaryFrame(), + aCaptureTarget, &status); + NS_ENSURE_SUCCESS_VOID(rv); } nsresult diff --git a/layout/base/tests/bug1078327_inner.html b/layout/base/tests/bug1078327_inner.html index 9e32fc996..0cfb9da7f 100644 --- a/layout/base/tests/bug1078327_inner.html +++ b/layout/base/tests/bug1078327_inner.html @@ -24,16 +24,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1078327 var test_mediator_move = false; var test_mediator_out = false; var test_listener = false; + var test_lost_capture = false; function TargetHandler(event) { logger("Target receive event: " + event.type + ". Mediator.setPointerCapture()"); mediator.setPointerCapture(event.pointerId); test_target = true; + test_capture = true; } function MediatorHandler(event) { logger("Mediator receive event: " + event.type); - if(event.type == "gotpointercapture") - test_capture = true; if(!test_capture) return; if(event.type == "pointermove") @@ -43,7 +43,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1078327 if(event.type == "pointerout") test_mediator_out++; if(event.type == "lostpointercapture") - test_capture = false; + test_lost_capture = true; } function ListenerHandler(event) { logger("Listener receive event: " + event.type); @@ -86,7 +86,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1078327 } function finishTest() { parent.is(test_target, true, "pointerdown event should be received by target"); - parent.is(test_capture, false, "test_capture should be false at the end of the test"); + parent.is(test_lost_capture, true, "mediator should receive lostpointercapture"); parent.is(test_mediator_over, 1, "mediator should receive pointerover event only once"); parent.is(test_mediator_move, 5, "mediator should receive pointermove event five times"); parent.is(test_mediator_out, 1, "mediator should receive pointerout event only once"); diff --git a/layout/base/tests/bug1162990_inner_1.html b/layout/base/tests/bug1162990_inner_1.html index 4ea5edb5c..0f950df8d 100644 --- a/layout/base/tests/bug1162990_inner_1.html +++ b/layout/base/tests/bug1162990_inner_1.html @@ -111,10 +111,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1162990 } function finishTest() { - parent.is(test_basketLeave, 0, "Part1: basket should not receive pointerleave event after pointer capturing"); + parent.is(test_basketLeave, 1, "Part1: basket should receive pointerleave event after pointer capturing"); parent.is(test_targetGotCapture, 1, "Part1: target should receive gotpointercapture event"); parent.is(test_targetLostCapture, 1, "Part1: target should receive lostpointercapture event"); - parent.is(test_targetLeave, 2, "Part1: target should receive pointerleave event two times"); + parent.is(test_targetLeave, 1, "Part1: target should receive pointerleave event only one time"); parent.is(test_childLeave, 0, "Part1: child should not receive pointerleave event after pointer capturing"); parent.is(test_listenerDown, 1, "Part1: listener should receive pointerdown event"); parent.is(test_listenerLeave, 1, "Part1: listener should receive pointerleave event only one time"); diff --git a/layout/base/tests/bug1162990_inner_2.html b/layout/base/tests/bug1162990_inner_2.html index 54aa74ca3..e418927bd 100644 --- a/layout/base/tests/bug1162990_inner_2.html +++ b/layout/base/tests/bug1162990_inner_2.html @@ -116,7 +116,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1162990 parent.is(test_targetDown, 1, "Part2: target should receive pointerdown event"); parent.is(test_targetGotCapture, 1, "Part2: target should receive gotpointercapture event"); parent.is(test_targetLostCapture, 1, "Part2: target should receive lostpointercapture event"); - parent.is(test_targetLeave, 1, "Part2: target should receive pointerleave event"); + parent.is(test_targetLeave, 0, "Part2: target should not receive pointerleave event"); parent.is(test_childLeave, 0, "Part2: child should not receive pointerleave event after pointer capturing"); parent.is(test_listenerLeave, 0, "Part2: listener should not receive pointerleave event after pointer capturing"); logger("finishTest"); diff --git a/layout/base/tests/bug976963_inner.html b/layout/base/tests/bug976963_inner.html deleted file mode 100644 index 2c55fbccd..000000000 --- a/layout/base/tests/bug976963_inner.html +++ /dev/null @@ -1,241 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=976963 ---> -<head> - <meta charset="utf-8"> - <title>Test for Bug 976963</title> - <meta name="author" content="Maksim Lebedev" /> - <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <style> - div#listener { - background: yellow; - position: absolute; - top: -100px; - } - div#middler { - background: yellow; - margin: 10px; - } - div#target { - background: yellow; - } - </style> - <script type="application/javascript"> - /** Test for Bug 976963 **/ - var All_Pointer_Events = ["pointerover", "pointerenter", - "pointermove", - "pointerdown", "pointerup", - "pointerout", "pointerleave", - "pointercancel", - "gotpointercapture", "lostpointercapture"]; - - function on_event(object, event, callback) { - object.addEventListener(event, callback, false); - } - function ok(check, msg) { - parent.ok(check, msg); - } - function is(a, b, msg) { - parent.is(a, b, msg); - } - - var listener = undefined; - var middler = undefined; - var target = undefined; - - var test_ListenerGotCapture = 0; - var test_ListenerUnwanted = 0; - var test_ListenerLostCapture = 0; - var test_ListenerAfterCapture = 0; - var test_MiddlerGotCapture = 0; - var test_MiddlerOver = 0; - var test_MiddlerLeave = 0; - var test_MiddlerUp = 0; - var test_MiddlerLostCapture = 0; - var test_TargetDown = 0; - var test_TargetUnwanted = 0; - var test_TargetUp = 0; - - var captured_event = undefined; - var f_gotPointerCapture = false; - var f_lostPointerCapture = false; - var f_gotMiddlerPointerCapture = false; - - function listenerEventHandler(event) { - logger("Listener: " + event.type + ". Captured_event: " + captured_event); - if(test_ListenerLostCapture) - test_ListenerAfterCapture++; - if (event.type == "gotpointercapture") { - f_gotPointerCapture = true; - test_ListenerGotCapture++; - } - else if (event.type == "lostpointercapture") { - f_lostPointerCapture = true; - f_gotPointerCapture = false; - test_ListenerLostCapture++; - } - else if (event.type == "pointermove") { - ok(captured_event && captured_event.pointerId == event.pointerId, "Listener: equals pointerId for lostpointercapture event"); - if (f_gotPointerCapture) { - // on first event received for capture, release capture - logger("Listener call release"); - ok(!!listener, "Listener should be live!"); - ok(typeof(listener.releasePointerCapture) == "function", "Listener should have a function releasePointerCapture"); - listener.releasePointerCapture(event.pointerId); - } - else { - logger("Listener.ASSERT: " + event.type); - test_ListenerUnwanted++; - // if any other events are received after releaseCapture, then the test fails - ok(false, event.target.id + "-" + event.type + " should be handled by target element handler"); - } - } - else { - test_ListenerUnwanted++; - logger("Listener.ASSERT: " + event.type); - ok(false, event.type + "should be never handled by listener"); - } - } - - function middlerEventHandler(event) { - logger("Middler: " + event.type + ". Captured_event: " + captured_event); - if (event.type == "gotpointercapture") { - test_MiddlerGotCapture++; - f_gotMiddlerPointerCapture = true; - ok(captured_event && captured_event.pointerId == event.pointerId, "Middler: equals pointerId for gotpointercapture event"); - } - else if (event.type == "pointerover") { - test_MiddlerOver++; - ok(captured_event && captured_event.pointerId == event.pointerId, "Middler: equals pointerId for pointerover event"); - } - else if (event.type == "pointerleave") { - test_MiddlerLeave++; - ok(captured_event && captured_event.pointerId == event.pointerId, "Middler: equals pointerId for pointerleave event"); - ok(!!listener, "Listener should be live!"); - ok(typeof(listener.setPointerCapture) == "function", "Listener should have a function setPointerCapture"); - listener.setPointerCapture(event.pointerId); - } - else if (event.type == "lostpointercapture") { - test_MiddlerLostCapture++; - f_gotMiddlerPointerCapture = false; - ok(captured_event && captured_event.pointerId == event.pointerId, "Middler: equals pointerId for lostpointercapture event"); - } - else if (event.type == "pointerup" ) { - test_MiddlerUp++; - } - } - - function targetEventHandler(event) { - logger("Target: " + event.type + ". Captured_event: " + captured_event); - if (f_gotPointerCapture || f_gotMiddlerPointerCapture) { - if (event.type != "pointerout" && event.type != "pointerleave") { - logger("Target.ASSERT: " + event.type + " " + event.pointerId); - test_TargetUnwanted++; - ok(false, "The Target element should not have received any events while capture is active. Event recieved:" + event.type + ". "); - } - } - if (event.type == "pointerdown") { - logger("Target.pointerdown 1: " + captured_event); - test_TargetDown++; - captured_event = event; - ok(!!middler, "Middler should be live!"); - ok(typeof(middler.setPointerCapture) == "function", "Middler should have a function setPointerCapture"); - middler.setPointerCapture(event.pointerId); - logger("Target.pointerdown 2: " + captured_event); - } - else if (event.type == "pointerup") { - ok(f_lostPointerCapture, "Target should have received pointerup"); - ok(captured_event && captured_event.pointerId == event.pointerId, "Target: equals pointerId for lostpointercapture event"); - test_TargetUp++; // complete test - } - } - - function colorerHandler(event) { - if(event.type == "pointerover") - event.target.style.background = "red"; - else if(event.type == "pointerout") - event.target.style.background = "yellow"; - } - - function setEventHandlers() { - listener = document.getElementById("listener"); - middler = document.getElementById("middler"); - target = document.getElementById("target"); - target.style["touchAction"] = "none"; - - // target and listener - handle all events - for (var i = 0; i < All_Pointer_Events.length; i++) { - on_event(target, All_Pointer_Events[i], targetEventHandler); - on_event(listener, All_Pointer_Events[i], listenerEventHandler); - on_event(middler, All_Pointer_Events[i], middlerEventHandler); - on_event(target, All_Pointer_Events[i], colorerHandler); - on_event(middler, All_Pointer_Events[i], colorerHandler); - } - } - - function prepareTest() { - SpecialPowers.pushPrefEnv({ - "set": [ - ["dom.w3c_pointer_events.enabled", true] - ] - }, executeTest); - } - - function executeTest() - { - logger("executeTest"); - setEventHandlers(); - document.body.offsetLeft; - var rect = target.getBoundingClientRect(); - synthesizePointer(target, rect.width/2, rect.height/2, {type: "pointermove"}); - synthesizePointer(target, rect.width/2, rect.height/2, {type: "pointerdown"}); - synthesizePointer(target, rect.width/3, rect.height/3, {type: "pointermove"}); - synthesizePointer(middler, rect.width/2, rect.height/2, {type: "pointermove"}); - synthesizePointer(target, rect.width/2, rect.height/2, {type: "pointermove"}); - synthesizePointer(middler, rect.width/2, rect.height/2, {type: "pointermove"}); - synthesizePointer(target, rect.width/2, rect.height/2, {type: "pointermove"}); - synthesizePointer(target, rect.width/2, rect.height/2, {type: "pointerup"}); - finishTest(); - } - - function finishTest() { - setTimeout(function() { - is(test_ListenerGotCapture, 1, "Listener should receive gotpointercapture event"); - is(test_ListenerUnwanted, 0, "Listener should not receive any unwanted events"); - is(test_ListenerLostCapture, 1, "Listener should receive lostpointercapture event"); - is(test_ListenerAfterCapture, 0, "Listener should not receive any events after release pointer capture"); - is(test_MiddlerGotCapture, 1, "Middler should receive gotpointercapture event"); - is(test_MiddlerOver, 1, "Middler should receive pointerover event"); - is(test_MiddlerLeave, 1, "Middler should receive pointerleave event"); - is(test_MiddlerUp, 0, "Middler should not receive pointerup event"); - is(test_MiddlerLostCapture, 1, "Middler should receive lostpointercapture event"); - is(test_TargetDown, 1, "Target should receive pointerdown event"); - is(test_TargetUnwanted, 0, "Target should not receive any event while pointer capture is active"); - is(test_TargetUp, 1, "Target should receive pointerup event"); - logger("finishTest"); - parent.finishTest(); - }, 1000); - } - - function logger(message) { - console.log(message); - var log = document.getElementById('log'); - log.innerHTML = message + "<br>" + log.innerHTML; - } - </script> -</head> -<body onload="prepareTest()"> - <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=976963">Mozilla Bug 976963</a> - <p id="display"></p> - <div id="content" style="display: none"> - </div> - <div id="listener">div id=listener</div> - <div id="middler">div id=middler</div> - <div id="target">div id=target</div> - <pre id="log"> - </pre> -</body> -</html> diff --git a/layout/base/tests/bug977003_inner_5.html b/layout/base/tests/bug977003_inner_5.html index 70fc5ba40..81094043c 100644 --- a/layout/base/tests/bug977003_inner_5.html +++ b/layout/base/tests/bug977003_inner_5.html @@ -26,6 +26,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1098139 var test_lost_listener = false; var test_lost_type = ""; var test_move_listener = false; + var test_over_listener = false; var test_listener = false; var test_lost_primary = false; @@ -53,6 +54,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1098139 logger("Receive event on Listener: " + event.type); test_move_listener = true; } + function ListenerOverHandler(event) { + logger("Receive event on Listener: " + event.type); + test_over_listener = true; + } function ListenerHandler(event) { logger("Receive event on Listener: " + event.type); test_listener = true; @@ -74,7 +79,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1098139 target.addEventListener("pointerdown", TargetDownHandler, false); listener.addEventListener("gotpointercapture", ListenerGotPCHandler, false); listener.addEventListener("lostpointercapture", ListenerLostPCHandler, false); - listener.addEventListener("pointerover", ListenerHandler, false); + listener.addEventListener("pointerover", ListenerOverHandler, false); listener.addEventListener("pointermove", ListenerMoveHandler, false); listener.addEventListener("pointerup", ListenerHandler, false); listener.addEventListener("pointerout", ListenerHandler, false); @@ -93,6 +98,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1098139 parent.is(test_lost_type, "touch", "Part 5: lostpointercapture event should have pointerType touch"); parent.is(test_lost_primary, true, "Part 5: lostpointercapture event should have isPrimary as true"); parent.is(test_move_listener, true, "Part 5: gotpointercapture should be triggered by pointermove"); + parent.is(test_over_listener, true, "Part 5: listener should receive pointerover when capturing pointer"); parent.is(test_listener, false, "Part 5: listener should not receive any other events"); logger("finishTest"); parent.finishTest(); diff --git a/layout/base/tests/bug977003_inner_6.html b/layout/base/tests/bug977003_inner_6.html index 12424b1f2..b60ca5c31 100644 --- a/layout/base/tests/bug977003_inner_6.html +++ b/layout/base/tests/bug977003_inner_6.html @@ -19,6 +19,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1073563 var listener = undefined; var test_target = false; var test_move = false; + var test_over = false; var test_listener = false; var receive_lostpointercapture = false; @@ -44,6 +45,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1073563 } } else if(event.type == "pointermove") { test_move = true; + } else if(event.type == "pointerover") { + test_over = true; } else { test_listener = true; } @@ -81,7 +84,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1073563 // PE level 2 defines that the pending pointer capture is processed when firing next pointer events. // In this test case, pointer capture release is processed when firing pointermove parent.is(test_move, true, "Part 6: gotpointercapture should be triggered by pointermove"); - parent.is(test_listener, false, "Part 6: no other pointerevents should be fired before gotpointercapture"); + parent.is(test_over, true, "Part 6: pointerover should be received when capturing pointer"); + parent.is(test_listener, false, "Part 6: no other pointerevents should be fired before gotpointercapture except pointerover"); logger("finishTest"); parent.finishTest(); } diff --git a/layout/base/tests/mochitest.ini b/layout/base/tests/mochitest.ini index 279b0af8a..405697977 100644 --- a/layout/base/tests/mochitest.ini +++ b/layout/base/tests/mochitest.ini @@ -256,8 +256,6 @@ skip-if = toolkit == 'android' support-files = bug851445_helper.html [test_bug970964.html] support-files = bug970964_inner.html -[test_bug976963.html] -support-files = bug976963_inner.html [test_bug977003.html] support-files = bug977003_inner_1.html diff --git a/layout/base/tests/test_bug976963.html b/layout/base/tests/test_bug976963.html deleted file mode 100644 index 4b8da3a6e..000000000 --- a/layout/base/tests/test_bug976963.html +++ /dev/null @@ -1,35 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=976963 ---> - <head> - <meta charset="utf-8"> - <meta name="author" content="Maksim Lebedev" /> - <title>Test for Bug 976963</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="text/javascript"> - function prepareTest() { - SimpleTest.waitForExplicitFinish(); - SimpleTest.requestFlakyTimeout("untriaged"); - SpecialPowers.pushPrefEnv({ - "set": [ - ["dom.w3c_pointer_events.enabled", true], - ["layout.reflow.synthMouseMove", false] - ] - }, startTest); - } - function startTest() { - var iframe = document.getElementById("testFrame"); - iframe.src = "bug976963_inner.html"; - } - function finishTest() { - SimpleTest.finish(); - } - </script> - </head> - <body onload="prepareTest()"> - <iframe id="testFrame" height="700" width="700"></iframe> - </body> -</html> |