summaryrefslogtreecommitdiffstats
path: root/layout/base
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:14:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:14:29 +0000
commitfbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 (patch)
tree4c1ccaf5486d4f2009f9a338a98a83e886e29c97 /layout/base
parentReleasing progress-linux version 124.0.1-1~progress7.99u1. (diff)
downloadfirefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.tar.xz
firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.zip
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'layout/base')
-rw-r--r--layout/base/LayoutTelemetryTools.h4
-rw-r--r--layout/base/MotionPathUtils.cpp8
-rw-r--r--layout/base/PositionedEventTargeting.cpp12
-rw-r--r--layout/base/PresShell.cpp204
-rw-r--r--layout/base/PresShell.h12
-rw-r--r--layout/base/RestyleManager.cpp9
-rw-r--r--layout/base/TouchManager.cpp130
-rw-r--r--layout/base/TouchManager.h16
-rw-r--r--layout/base/crashtests/1435015.html9
-rw-r--r--layout/base/crashtests/243159-2.xhtml26
-rw-r--r--layout/base/crashtests/355993-1.xhtml26
-rw-r--r--layout/base/crashtests/384649-1.xhtml31
-rw-r--r--layout/base/crashtests/394150-1.xhtml27
-rw-r--r--layout/base/crashtests/399676-1.xhtml7
-rw-r--r--layout/base/crashtests/400445-1.xhtml22
-rw-r--r--layout/base/crashtests/400904-1.xhtml20
-rw-r--r--layout/base/crashtests/crashtests.list12
-rw-r--r--layout/base/nsBidiPresUtils.cpp247
-rw-r--r--layout/base/nsBidiPresUtils.h6
-rw-r--r--layout/base/nsCSSFrameConstructor.cpp240
-rw-r--r--layout/base/nsCSSFrameConstructor.h51
-rw-r--r--layout/base/nsDocumentViewer.cpp6
-rw-r--r--layout/base/nsLayoutUtils.cpp14
-rw-r--r--layout/base/nsLayoutUtils.h8
-rw-r--r--layout/base/nsPresContext.cpp1
-rw-r--r--layout/base/nsPresContext.h3
-rw-r--r--layout/base/nsRefreshDriver.cpp3
-rw-r--r--layout/base/tests/mochitest.toml2
-rw-r--r--layout/base/tests/test_bug1836801.html59
-rw-r--r--layout/base/tests/test_event_target_iframe_oop.html177
30 files changed, 656 insertions, 736 deletions
diff --git a/layout/base/LayoutTelemetryTools.h b/layout/base/LayoutTelemetryTools.h
index 25c80dcc2c..43eee79923 100644
--- a/layout/base/LayoutTelemetryTools.h
+++ b/layout/base/LayoutTelemetryTools.h
@@ -38,9 +38,9 @@ enum class LayoutSubsystem : uint8_t {
};
using LayoutSubsystemDurations =
- EnumeratedArray<LayoutSubsystem, LayoutSubsystem::Count, double>;
+ EnumeratedArray<LayoutSubsystem, double, size_t(LayoutSubsystem::Count)>;
using LayoutFlushCount =
- EnumeratedArray<FlushKind, FlushKind::Count, SaturateUint8>;
+ EnumeratedArray<FlushKind, SaturateUint8, size_t(FlushKind::Count)>;
struct Data {
Data();
diff --git a/layout/base/MotionPathUtils.cpp b/layout/base/MotionPathUtils.cpp
index c81020645d..4045f2304a 100644
--- a/layout/base/MotionPathUtils.cpp
+++ b/layout/base/MotionPathUtils.cpp
@@ -111,9 +111,11 @@ CSSCoord MotionPathUtils::GetRayContainReferenceSize(nsIFrame* aFrame) {
const auto size = CSSSize::FromAppUnits(
(aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)
? nsLayoutUtils::ComputeSVGReferenceRect(
- aFrame, aFrame->StyleSVGReset()->HasNonScalingStroke()
- ? StyleGeometryBox::FillBox
- : StyleGeometryBox::StrokeBox)
+ aFrame,
+ aFrame->StyleSVGReset()->HasNonScalingStroke()
+ ? StyleGeometryBox::FillBox
+ : StyleGeometryBox::StrokeBox,
+ nsLayoutUtils::MayHaveNonScalingStrokeCyclicDependency::Yes)
: nsLayoutUtils::ComputeHTMLReferenceRect(
aFrame, StyleGeometryBox::BorderBox))
.Size());
diff --git a/layout/base/PositionedEventTargeting.cpp b/layout/base/PositionedEventTargeting.cpp
index e159239f16..a975372dea 100644
--- a/layout/base/PositionedEventTargeting.cpp
+++ b/layout/base/PositionedEventTargeting.cpp
@@ -249,18 +249,6 @@ static nsIContent* GetClickableAncestor(
return content;
}
- // Bug 921928: we don't have access to the content of remote iframe.
- // So fluffing won't go there. We do an optimistic assumption here:
- // that the content of the remote iframe needs to be a target.
- if (content->IsHTMLElement(nsGkAtoms::iframe) &&
- content->AsElement()->AttrValueIs(kNameSpaceID_None,
- nsGkAtoms::mozbrowser,
- nsGkAtoms::_true, eIgnoreCase) &&
- content->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote,
- nsGkAtoms::_true, eIgnoreCase)) {
- return content;
- }
-
// See nsCSSFrameConstructor::FindXULTagData. This code is not
// really intended to be used with XUL, though.
if (content->IsAnyOfXULElements(
diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
index 90a9eee411..31c21c3377 100644
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -9,11 +9,13 @@
#include "mozilla/PresShell.h"
#include "Units.h"
+#include "mozilla/EventForwards.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/AncestorIterator.h"
#include "mozilla/dom/FontFaceSet.h"
#include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/LargestContentfulPaint.h"
+#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/dom/PerformanceMainThread.h"
#include "mozilla/dom/HTMLAreaElement.h"
#include "mozilla/ArrayUtils.h"
@@ -45,6 +47,7 @@
#include "mozilla/StaticPrefs_font.h"
#include "mozilla/StaticPrefs_image.h"
#include "mozilla/StaticPrefs_layout.h"
+#include "mozilla/StaticPrefs_test.h"
#include "mozilla/StaticPrefs_toolkit.h"
#include "mozilla/Try.h"
#include "mozilla/TextEvents.h"
@@ -562,35 +565,52 @@ class nsBeforeFirstPaintDispatcher : public Runnable {
class MOZ_STACK_CLASS AutoPointerEventTargetUpdater final {
public:
AutoPointerEventTargetUpdater(PresShell* aShell, WidgetEvent* aEvent,
- nsIFrame* aFrame, nsIContent** aTargetContent) {
+ nsIFrame* aFrame, nsIContent* aTargetContent,
+ nsIContent** aOutTargetContent) {
MOZ_ASSERT(aEvent);
- if (!aTargetContent || aEvent->mClass != ePointerEventClass) {
+ if (!aOutTargetContent || aEvent->mClass != ePointerEventClass) {
// Make the destructor happy.
- mTargetContent = nullptr;
+ mOutTargetContent = nullptr;
return;
}
MOZ_ASSERT(aShell);
- MOZ_ASSERT(aFrame);
- MOZ_ASSERT(!aFrame->GetContent() ||
- aShell->GetDocument() == aFrame->GetContent()->OwnerDoc());
+ MOZ_ASSERT_IF(aFrame && aFrame->GetContent(),
+ aShell->GetDocument() == aFrame->GetContent()->OwnerDoc());
mShell = aShell;
mWeakFrame = aFrame;
- mTargetContent = aTargetContent;
- aShell->mPointerEventTarget = aFrame->GetContent();
+ mOutTargetContent = aOutTargetContent;
+ mFromTouch = aEvent->AsPointerEvent()->mFromTouchEvent;
+ // Touch event target may have no frame, e.g., removed from the DOM
+ MOZ_ASSERT_IF(!mFromTouch, aFrame);
+ mOriginalPointerEventTarget = aShell->mPointerEventTarget =
+ aFrame ? aFrame->GetContent() : aTargetContent;
}
~AutoPointerEventTargetUpdater() {
- if (!mTargetContent || !mShell || mWeakFrame.IsAlive()) {
+ if (!mOutTargetContent || !mShell || mWeakFrame.IsAlive()) {
return;
}
- mShell->mPointerEventTarget.swap(*mTargetContent);
+ if (mFromTouch) {
+ // If the source event is a touch event, the touch event target should
+ // always be same target as preceding ePointerDown. Therefore, we should
+ // always set it back to the original event target.
+ mOriginalPointerEventTarget.swap(*mOutTargetContent);
+ } else {
+ // If the source event is not a touch event (must be a mouse event in
+ // this case), the event should be fired on the closest inclusive ancestor
+ // of the pointer event target which is still connected. The mutations
+ // are tracked by PresShell::ContentRemoved. Therefore, we should set it.
+ mShell->mPointerEventTarget.swap(*mOutTargetContent);
+ }
}
private:
RefPtr<PresShell> mShell;
+ nsCOMPtr<nsIContent> mOriginalPointerEventTarget;
AutoWeakFrame mWeakFrame;
- nsIContent** mTargetContent;
+ nsIContent** mOutTargetContent;
+ bool mFromTouch = false;
};
bool PresShell::sDisableNonTestMouseEvents = false;
@@ -5421,7 +5441,6 @@ bool PresShell::IsTransparentContainerElement() const {
case dom::PrefersColorSchemeOverride::Dark:
return pc->DefaultBackgroundColorScheme() == ColorScheme::Dark;
case dom::PrefersColorSchemeOverride::None:
- case dom::PrefersColorSchemeOverride::EndGuard_:
break;
}
}
@@ -6888,18 +6907,13 @@ PresShell* PresShell::GetShellForTouchEvent(WidgetGUIEvent* aEvent) {
return nullptr;
}
- nsCOMPtr<nsIContent> content = do_QueryInterface(oldTouch->GetTarget());
+ nsIContent* const content =
+ nsIContent::FromEventTargetOrNull(oldTouch->GetTarget());
if (!content) {
return nullptr;
}
- nsIFrame* contentFrame = content->GetPrimaryFrame();
- if (!contentFrame) {
- return nullptr;
- }
-
- PresShell* presShell = contentFrame->PresContext()->GetPresShell();
- if (presShell) {
+ if (PresShell* const presShell = content->OwnerDoc()->GetPresShell()) {
return presShell;
}
}
@@ -7249,12 +7263,6 @@ nsresult PresShell::EventHandler::HandleEventUsingCoordinates(
return NS_OK;
}
- // frame could be null after dispatching pointer events.
- // XXX Despite of this comment, we update the event target data outside
- // DispatchPrecedingPointerEvent(). Can we make it call
- // UpdateTouchEventTarget()?
- eventTargetData.UpdateTouchEventTarget(aGUIEvent);
-
// 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
@@ -7267,7 +7275,17 @@ nsresult PresShell::EventHandler::HandleEventUsingCoordinates(
nsresult rv = eventHandler.HandleEventWithCurrentEventInfo(
aGUIEvent, aEventStatus, true,
MOZ_KnownLive(eventTargetData.mOverrideClickTarget));
- return rv;
+ if (NS_FAILED(rv) ||
+ MOZ_UNLIKELY(eventTargetData.mPresShell->IsDestroying())) {
+ return rv;
+ }
+
+ if (aGUIEvent->mMessage == eTouchEnd) {
+ MaybeSynthesizeCompatMouseEventsForTouchEnd(aGUIEvent->AsTouchEvent(),
+ aEventStatus);
+ }
+
+ return NS_OK;
}
bool PresShell::EventHandler::MaybeFlushPendingNotifications(
@@ -7436,41 +7454,58 @@ bool PresShell::EventHandler::DispatchPrecedingPointerEvent(
AutoWeakFrame weakTargetFrame(targetFrame);
AutoWeakFrame weakFrame(aEventTargetData->GetFrame());
- nsCOMPtr<nsIContent> content(aEventTargetData->GetContent());
+ nsCOMPtr<nsIContent> pointerEventTargetContent(
+ aEventTargetData->GetContent());
RefPtr<PresShell> presShell(aEventTargetData->mPresShell);
- nsCOMPtr<nsIContent> targetContent;
+ nsCOMPtr<nsIContent> mouseOrTouchEventTargetContent;
PointerEventHandler::DispatchPointerFromMouseOrTouch(
- presShell, aEventTargetData->GetFrame(), content, aGUIEvent,
- aDontRetargetEvents, aEventStatus, getter_AddRefs(targetContent));
+ presShell, aEventTargetData->GetFrame(), pointerEventTargetContent,
+ aGUIEvent, aDontRetargetEvents, aEventStatus,
+ getter_AddRefs(mouseOrTouchEventTargetContent));
// 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 (weakTargetFrame.IsAlive() && weakFrame.IsAlive()) {
+ aEventTargetData->UpdateTouchEventTarget(aGUIEvent);
+ return true;
}
- // If the event is not a mouse event, the caller should keep handling the
- // event unless event target frame is destroyed. Note that this case is
- // not defined by the spec.
- if (aGUIEvent->mClass != eMouseEventClass) {
- return weakFrame.IsAlive();
+ presShell->FlushPendingNotifications(FlushType::Layout);
+ if (MOZ_UNLIKELY(mPresShell->IsDestroying())) {
+ return false;
}
- // Spec defines that mouse events must be dispatched to the same target as
- // the pointer event. If the target is no longer participating in its
- // ownerDocument's tree, fire the event at the original target's nearest
- // ancestor node
- if (!targetContent) {
+ // The spec defines that mouse events must be dispatched to the same target as
+ // the pointer event.
+ // The Touch Events spec defines that touch events must be dispatched to the
+ // same target as touch start and the other browsers dispatch touch events
+ // even if the touch event target is not connected to the document.
+ // Retargetting the event is handled by AutoPointerEventTargetUpdater and
+ // mouseOrTouchEventTargetContent stores the result.
+
+ // If the target is no longer participating in its ownerDocument's tree,
+ // fire the event at the original target's nearest ancestor node.
+ if (!mouseOrTouchEventTargetContent) {
+ MOZ_ASSERT(aGUIEvent->mClass == eMouseEventClass);
return false;
}
- aEventTargetData->SetFrameAndContent(targetContent->GetPrimaryFrame(),
- targetContent);
- aEventTargetData->mPresShell = PresShell::GetShellForEventTarget(
- aEventTargetData->GetFrame(), aEventTargetData->GetContent());
+ aEventTargetData->SetFrameAndContent(
+ mouseOrTouchEventTargetContent->GetPrimaryFrame(),
+ mouseOrTouchEventTargetContent);
+ aEventTargetData->mPresShell =
+ mouseOrTouchEventTargetContent->IsInComposedDoc()
+ ? PresShell::GetShellForEventTarget(aEventTargetData->GetFrame(),
+ aEventTargetData->GetContent())
+ : mouseOrTouchEventTargetContent->OwnerDoc()->GetPresShell();
// If new target PresShel is not found, we cannot keep handling the event.
- return !!aEventTargetData->mPresShell;
+ if (!aEventTargetData->mPresShell) {
+ return false;
+ }
+
+ aEventTargetData->UpdateTouchEventTarget(aGUIEvent);
+ return true;
}
/**
@@ -7586,6 +7621,60 @@ bool PresShell::EventHandler::MaybeHandleEventWithAccessibleCaret(
return true;
}
+void PresShell::EventHandler::MaybeSynthesizeCompatMouseEventsForTouchEnd(
+ const WidgetTouchEvent* aTouchEndEvent,
+ const nsEventStatus* aStatus) const {
+ MOZ_ASSERT(aTouchEndEvent->mMessage == eTouchEnd);
+
+ // If the eTouchEnd event is dispatched via APZ, APZCCallbackHelper dispatches
+ // a set of mouse events with better handling. Therefore, we don't need to
+ // handle that here.
+ if (!aTouchEndEvent->mFlags.mIsSynthesizedForTests ||
+ StaticPrefs::test_events_async_enabled()) {
+ return;
+ }
+
+ // If the tap was consumed or 2 or more touches occurred, we don't need the
+ // compatibility mouse events.
+ if (*aStatus == nsEventStatus_eConsumeNoDefault ||
+ !TouchManager::IsSingleTapEndToDoDefault(aTouchEndEvent)) {
+ return;
+ }
+
+ if (NS_WARN_IF(!aTouchEndEvent->mWidget)) {
+ return;
+ }
+
+ nsCOMPtr<nsIWidget> widget = aTouchEndEvent->mWidget;
+
+ // NOTE: I think that we don't need to implement a double click here becase
+ // WebDriver does not support a way to synthesize a double click and Chrome
+ // does not fire "dblclick" even if doing `pointerDown().pointerUp()` twice.
+ // FIXME: Currently we don't support long tap.
+ RefPtr<PresShell> presShell = mPresShell;
+ for (const EventMessage message : {eMouseMove, eMouseDown, eMouseUp}) {
+ if (MOZ_UNLIKELY(presShell->IsDestroying())) {
+ break;
+ }
+ nsIFrame* frameForPresShell = GetNearestFrameContainingPresShell(presShell);
+ if (!frameForPresShell) {
+ break;
+ }
+ WidgetMouseEvent event(true, message, widget, WidgetMouseEvent::eReal,
+ WidgetMouseEvent::eNormal);
+ event.mRefPoint = aTouchEndEvent->mTouches[0]->mRefPoint;
+ event.mButton = MouseButton::ePrimary;
+ event.mButtons = message == eMouseDown ? MouseButtonsFlag::ePrimaryFlag
+ : MouseButtonsFlag::eNoButtons;
+ event.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
+ event.mClickCount = message == eMouseMove ? 0 : 1;
+ event.mModifiers = aTouchEndEvent->mModifiers;
+ event.convertToPointer = false;
+ nsEventStatus mouseEventStatus = nsEventStatus_eIgnore;
+ presShell->HandleEvent(frameForPresShell, &event, false, &mouseEventStatus);
+ }
+}
+
bool PresShell::EventHandler::MaybeDiscardEvent(WidgetGUIEvent* aGUIEvent) {
MOZ_ASSERT(aGUIEvent);
@@ -8252,7 +8341,7 @@ nsresult PresShell::EventHandler::HandleEventWithTarget(
mPresShell->RecordPointerLocation(aEvent->AsMouseEvent());
}
AutoPointerEventTargetUpdater updater(mPresShell, aEvent, aNewEventFrame,
- aTargetContent);
+ aNewEventContent, aTargetContent);
AutoCurrentEventInfoSetter eventInfoSetter(*this, aNewEventFrame,
aNewEventContent);
nsresult rv = HandleEventWithCurrentEventInfo(aEvent, aEventStatus, false,
@@ -8360,7 +8449,7 @@ nsresult PresShell::EventHandler::HandleEventWithCurrentEventInfo(
manager->TryToFlushPendingNotificationsToIME();
}
- FinalizeHandlingEvent(aEvent);
+ FinalizeHandlingEvent(aEvent, aEventStatus);
RecordEventHandlingResponsePerformance(aEvent);
@@ -8512,7 +8601,8 @@ bool PresShell::EventHandler::PrepareToDispatchEvent(
}
}
-void PresShell::EventHandler::FinalizeHandlingEvent(WidgetEvent* aEvent) {
+void PresShell::EventHandler::FinalizeHandlingEvent(
+ WidgetEvent* aEvent, const nsEventStatus* aStatus) {
switch (aEvent->mMessage) {
case eKeyPress:
case eKeyDown:
@@ -8562,6 +8652,16 @@ void PresShell::EventHandler::FinalizeHandlingEvent(WidgetEvent* aEvent) {
}
return;
}
+ case eTouchStart:
+ case eTouchMove:
+ case eTouchEnd:
+ case eTouchCancel:
+ case eTouchPointerCancel:
+ case eMouseLongTap:
+ case eContextMenu: {
+ mPresShell->mTouchManager.PostHandleEvent(aEvent, aStatus);
+ break;
+ }
default:
return;
}
@@ -11955,7 +12055,7 @@ void PresShell::EventHandler::EventTargetData::UpdateTouchEventTarget(
nsIFrame* newFrame =
TouchManager::SuppressInvalidPointsAndGetTargetedFrame(touchEvent);
if (!newFrame) {
- return; // XXX Why don't we stop handling the event in this case?
+ return;
}
SetFrameAndComputePresShellAndContent(newFrame, aGUIEvent);
return;
diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h
index c6a965e81e..482ace1421 100644
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -2440,6 +2440,14 @@ class PresShell final : public nsStubDocumentObserver,
nsEventStatus* aEventStatus);
/**
+ * Maybe dispatch mouse events for aTouchEnd. This should be called after
+ * aTouchEndEvent is dispatched into the DOM.
+ */
+ MOZ_CAN_RUN_SCRIPT void MaybeSynthesizeCompatMouseEventsForTouchEnd(
+ const WidgetTouchEvent* aTouchEndEvent,
+ const nsEventStatus* aStatus) const;
+
+ /**
* MaybeDiscardOrDelayKeyboardEvent() may discared or put aGUIEvent into
* the delayed event queue if it's a keyboard event and if we should do so.
* If aGUIEvent is not a keyboard event, this does nothing.
@@ -2821,8 +2829,10 @@ class PresShell final : public nsStubDocumentObserver,
* and then, this cleans up the state of mPresShell and aEvent.
*
* @param aEvent The handled event.
+ * @param aStatus The status of aEvent. Must not be nullptr.
*/
- MOZ_CAN_RUN_SCRIPT void FinalizeHandlingEvent(WidgetEvent* aEvent);
+ MOZ_CAN_RUN_SCRIPT void FinalizeHandlingEvent(WidgetEvent* aEvent,
+ const nsEventStatus* aStatus);
/**
* AutoCurrentEventInfoSetter() pushes and pops current event info of
diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp
index 9c313a254c..8c07512093 100644
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -2709,6 +2709,7 @@ enum class ServoPostTraversalFlags : uint32_t {
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ServoPostTraversalFlags)
+#ifdef ACCESSIBILITY
static bool IsVisibleForA11y(const ComputedStyle& aStyle) {
return aStyle.StyleVisibility()->IsVisible() && !aStyle.StyleUI()->IsInert();
}
@@ -2717,6 +2718,7 @@ static bool IsSubtreeVisibleForA11y(const ComputedStyle& aStyle) {
return aStyle.StyleDisplay()->mContentVisibility !=
StyleContentVisibility::Hidden;
}
+#endif
// Send proper accessibility notifications and return post traversal
// flags for kids.
@@ -3450,11 +3452,8 @@ void RestyleManager::MaybeRestyleForNthOfState(ServoStyleSet& aStyleSet,
static inline bool AttributeInfluencesOtherPseudoClassState(
const Element& aElement, const nsAtom* aAttribute) {
- // We must record some state for :-moz-browser-frame,
- // :-moz-table-border-nonzero, and :-moz-select-list-box.
- if (aAttribute == nsGkAtoms::mozbrowser) {
- return aElement.IsAnyOfHTMLElements(nsGkAtoms::iframe, nsGkAtoms::frame);
- }
+ // We must record some state for :-moz-table-border-nonzero and
+ // :-moz-select-list-box.
if (aAttribute == nsGkAtoms::border) {
return aElement.IsHTMLElement(nsGkAtoms::table);
diff --git a/layout/base/TouchManager.cpp b/layout/base/TouchManager.cpp
index 0c34567b65..42ea78eb2e 100644
--- a/layout/base/TouchManager.cpp
+++ b/layout/base/TouchManager.cpp
@@ -7,9 +7,13 @@
#include "TouchManager.h"
+#include "Units.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/StaticPrefs_test.h"
+#include "mozilla/TimeStamp.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/EventTarget.h"
-#include "mozilla/PresShell.h"
#include "mozilla/layers/InputAPZContext.h"
#include "nsIContent.h"
#include "nsIFrame.h"
@@ -24,6 +28,8 @@ namespace mozilla {
StaticAutoPtr<nsTHashMap<nsUint32HashKey, TouchManager::TouchInfo>>
TouchManager::sCaptureTouchList;
layers::LayersId TouchManager::sCaptureTouchLayersId;
+TimeStamp TouchManager::sSingleTouchStartTimeStamp;
+LayoutDeviceIntPoint TouchManager::sSingleTouchStartPoint;
/*static*/
void TouchManager::InitializeStatics() {
@@ -168,7 +174,7 @@ nsIFrame* TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
}
nsIFrame* frame = nullptr;
- for (int32_t i = aEvent->mTouches.Length(); i;) {
+ for (uint32_t i = aEvent->mTouches.Length(); i;) {
--i;
dom::Touch* touch = aEvent->mTouches[i];
if (TouchManager::HasCapturedTouch(touch->Identifier())) {
@@ -176,9 +182,35 @@ nsIFrame* TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
}
MOZ_ASSERT(touch->mOriginalTarget);
- nsCOMPtr<nsIContent> targetContent = do_QueryInterface(touch->GetTarget());
- nsIFrame* targetFrame =
- targetContent ? targetContent->GetPrimaryFrame() : nullptr;
+ nsIContent* const targetContent =
+ nsIContent::FromEventTargetOrNull(touch->GetTarget());
+ if (MOZ_UNLIKELY(!targetContent)) {
+ touch->mIsTouchEventSuppressed = true;
+ continue;
+ }
+
+ // Even if the target content is not connected, we should dispatch the touch
+ // start event except when the target content is owned by different
+ // document.
+ if (MOZ_UNLIKELY(!targetContent->IsInComposedDoc())) {
+ if (anyTarget && anyTarget->OwnerDoc() != targetContent->OwnerDoc()) {
+ touch->mIsTouchEventSuppressed = true;
+ continue;
+ }
+ if (!anyTarget) {
+ anyTarget = targetContent;
+ }
+ touch->SetTouchTarget(targetContent->GetAsElementOrParentElement());
+ if (PresShell* const presShell =
+ targetContent->OwnerDoc()->GetPresShell()) {
+ if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
+ frame = rootFrame;
+ }
+ }
+ continue;
+ }
+
+ nsIFrame* targetFrame = targetContent->GetPrimaryFrame();
if (targetFrame && !anyTarget) {
anyTarget = targetContent;
} else {
@@ -202,10 +234,12 @@ nsIFrame* TouchManager::SuppressInvalidPointsAndGetTargetedFrame(
touch->mIsTouchEventSuppressed = true;
} else {
targetFrame = newTargetFrame;
- targetFrame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
- touch->SetTouchTarget(targetContent
- ? targetContent->GetAsElementOrParentElement()
- : nullptr);
+ nsCOMPtr<nsIContent> newTargetContent;
+ targetFrame->GetContentForEvent(aEvent,
+ getter_AddRefs(newTargetContent));
+ touch->SetTouchTarget(
+ newTargetContent ? newTargetContent->GetAsElementOrParentElement()
+ : nullptr);
}
}
if (targetFrame) {
@@ -236,8 +270,11 @@ bool TouchManager::PreHandleEvent(WidgetEvent* aEvent, nsEventStatus* aStatus,
// touch event associated to. We cache layers id of the first touchstart
// event, all subsequent touch events will use the same layers id.
sCaptureTouchLayersId = aEvent->mLayersId;
+ sSingleTouchStartTimeStamp = aEvent->mTimeStamp;
+ sSingleTouchStartPoint = aEvent->AsTouchEvent()->mTouches[0]->mRefPoint;
} else {
touchEvent->mLayersId = sCaptureTouchLayersId;
+ sSingleTouchStartTimeStamp = TimeStamp();
}
// Add any new touches to the queue
WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
@@ -404,6 +441,60 @@ bool TouchManager::PreHandleEvent(WidgetEvent* aEvent, nsEventStatus* aStatus,
return true;
}
+void TouchManager::PostHandleEvent(const WidgetEvent* aEvent,
+ const nsEventStatus* aStatus) {
+ switch (aEvent->mMessage) {
+ case eTouchMove: {
+ if (sSingleTouchStartTimeStamp.IsNull()) {
+ break;
+ }
+ if (*aStatus == nsEventStatus_eConsumeNoDefault) {
+ sSingleTouchStartTimeStamp = TimeStamp();
+ break;
+ }
+ const WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+ if (touchEvent->mTouches.Length() > 1) {
+ sSingleTouchStartTimeStamp = TimeStamp();
+ break;
+ }
+ if (touchEvent->mTouches.Length() == 1) {
+ // If the touch moved too far from the start point, don't treat the
+ // touch as a tap.
+ const float distance =
+ static_cast<float>((sSingleTouchStartPoint -
+ aEvent->AsTouchEvent()->mTouches[0]->mRefPoint)
+ .Length());
+ const float maxDistance =
+ StaticPrefs::apz_touch_start_tolerance() *
+ (MOZ_LIKELY(touchEvent->mWidget) ? touchEvent->mWidget->GetDPI()
+ : 96.0f);
+ if (distance > maxDistance) {
+ sSingleTouchStartTimeStamp = TimeStamp();
+ }
+ }
+ break;
+ }
+ case eTouchStart:
+ case eTouchEnd:
+ if (*aStatus == nsEventStatus_eConsumeNoDefault &&
+ !sSingleTouchStartTimeStamp.IsNull()) {
+ sSingleTouchStartTimeStamp = TimeStamp();
+ }
+ break;
+ case eTouchCancel:
+ case eTouchPointerCancel:
+ case eMouseLongTap:
+ case eContextMenu: {
+ if (!sSingleTouchStartTimeStamp.IsNull()) {
+ sSingleTouchStartTimeStamp = TimeStamp();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
/*static*/
already_AddRefed<nsIContent> TouchManager::GetAnyCapturedTouchTarget() {
nsCOMPtr<nsIContent> result = nullptr;
@@ -473,4 +564,25 @@ bool TouchManager::ShouldConvertTouchToPointer(const Touch* aTouch,
return true;
}
+/* static */
+bool TouchManager::IsSingleTapEndToDoDefault(
+ const WidgetTouchEvent* aTouchEndEvent) {
+ MOZ_ASSERT(aTouchEndEvent);
+ MOZ_ASSERT(aTouchEndEvent->mFlags.mIsSynthesizedForTests);
+ MOZ_ASSERT(!StaticPrefs::test_events_async_enabled());
+ if (sSingleTouchStartTimeStamp.IsNull() ||
+ aTouchEndEvent->mTouches.Length() != 1) {
+ return false;
+ }
+ // If it's pressed long time, we should not treat it as a single tap because
+ // a long press should cause opening context menu by default.
+ if ((aTouchEndEvent->mTimeStamp - sSingleTouchStartTimeStamp)
+ .ToMilliseconds() > StaticPrefs::apz_max_tap_time()) {
+ return false;
+ }
+ NS_WARNING_ASSERTION(aTouchEndEvent->mTouches[0]->mChanged,
+ "The single tap end should be changed");
+ return true;
+}
+
} // namespace mozilla
diff --git a/layout/base/TouchManager.h b/layout/base/TouchManager.h
index 0e12c25c7d..40b9083bdb 100644
--- a/layout/base/TouchManager.h
+++ b/layout/base/TouchManager.h
@@ -12,6 +12,7 @@
#ifndef TouchManager_h_
#define TouchManager_h_
+#include "Units.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/dom/Touch.h"
#include "mozilla/StaticPtr.h"
@@ -20,6 +21,7 @@
namespace mozilla {
class PresShell;
+class TimeStamp;
class TouchManager {
public:
@@ -52,6 +54,8 @@ class TouchManager {
bool PreHandleEvent(mozilla::WidgetEvent* aEvent, nsEventStatus* aStatus,
bool& aTouchIsNew,
nsCOMPtr<nsIContent>& aCurrentEventContent);
+ void PostHandleEvent(const mozilla::WidgetEvent* aEvent,
+ const nsEventStatus* aStatus);
static already_AddRefed<nsIContent> GetAnyCapturedTouchTarget();
static bool HasCapturedTouch(int32_t aId);
@@ -59,6 +63,12 @@ class TouchManager {
static bool ShouldConvertTouchToPointer(const dom::Touch* aTouch,
const WidgetTouchEvent* aEvent);
+ // This should be called after PostHandleEvent() is called. Note that this
+ // cannot check touches outside this process. So, this should not be used for
+ // actual user input handling. This is designed for a fallback path to
+ // dispatch mouse events for touch events synthesized without APZ.
+ static bool IsSingleTapEndToDoDefault(const WidgetTouchEvent* aTouchEndEvent);
+
private:
void EvictTouches(dom::Document* aLimitToDocument = nullptr);
static void EvictTouchPoint(RefPtr<dom::Touch>& aTouch,
@@ -77,6 +87,12 @@ class TouchManager {
static StaticAutoPtr<nsTHashMap<nsUint32HashKey, TouchInfo>>
sCaptureTouchList;
static layers::LayersId sCaptureTouchLayersId;
+ // The last start of a single tap. This will be set to "Null" if the tap is
+ // consumed or becomes not a single tap.
+ static TimeStamp sSingleTouchStartTimeStamp;
+ // The last start point of the single tap tracked with
+ // sSingleTouchStartTimeStamp.
+ static LayoutDeviceIntPoint sSingleTouchStartPoint;
};
} // namespace mozilla
diff --git a/layout/base/crashtests/1435015.html b/layout/base/crashtests/1435015.html
deleted file mode 100644
index 329aaca22f..0000000000
--- a/layout/base/crashtests/1435015.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!doctype html>
-<style>
- div { display: contents; }
-</style>
-<math></math>
-<script>
- let div = document.createElementNS('http://www.w3.org/1998/Math/MathML', 'div');
- document.querySelector('math').appendChild(div);
-</script>
diff --git a/layout/base/crashtests/243159-2.xhtml b/layout/base/crashtests/243159-2.xhtml
deleted file mode 100644
index 79d9bcd90a..0000000000
--- a/layout/base/crashtests/243159-2.xhtml
+++ /dev/null
@@ -1,26 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:mathml="http://www.w3.org/1998/Math/MathML">
- <body onload="run()">
- <mathml:math id="test" style="display: block">
- </mathml:math>
-<script>
- function run() {
- var t1 = document.createElementNS("http://www.w3.org/1998/Math/MathML",
- "mtable");
- var t2 = document.createElementNS("http://www.w3.org/1998/Math/MathML",
- "mtable");
- var r1 = document.createElementNS("http://www.w3.org/1998/Math/MathML",
- "mtr");
- var r2 = document.createElementNS("http://www.w3.org/1998/Math/MathML",
- "mtr");
- var test =
- document.getElementsByTagNameNS("http://www.w3.org/1998/Math/MathML", "math")[0];
- t1.appendChild(r1);
- test.appendChild(t1);
- test.appendChild(t2);
- t2.appendChild(r2);
-
- }
-</script>
-</body>
-</html>
diff --git a/layout/base/crashtests/355993-1.xhtml b/layout/base/crashtests/355993-1.xhtml
deleted file mode 100644
index e902ee550e..0000000000
--- a/layout/base/crashtests/355993-1.xhtml
+++ /dev/null
@@ -1,26 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml">
-
-<head>
-<style>
-body, body * { position: fixed; }
-</style>
-</head>
-
-<body>
-
-
-<div>
- <math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
-
- <mtable>
- <mtr>
- <mtd>
- <mn>1</mn>
- </mtd>
- </mtr>
- </mtable>
- </math>
-</div>
-
-</body>
-</html> \ No newline at end of file
diff --git a/layout/base/crashtests/384649-1.xhtml b/layout/base/crashtests/384649-1.xhtml
deleted file mode 100644
index e2ba50cdee..0000000000
--- a/layout/base/crashtests/384649-1.xhtml
+++ /dev/null
@@ -1,31 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<style>
-
-/* use attribute selector instead of the .class shorthand to work around bug 379178 */
-
-*[class="fixed"] { position: fixed; }
-
-math, mtable, mtr { position: inherit; }
-
-</style>
-</head>
-
-<body>
-
-<div class="fixed">
- <math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
- <mtable>
- <mtr class="fixed">
- <mtd><mi>x</mi></mtd>
- </mtr>
- <mtr>
- <mtd><mi>y</mi></mtd>
- </mtr>
- </mtable>
- </math>
-</div>
-
-</body>
-
-</html>
diff --git a/layout/base/crashtests/394150-1.xhtml b/layout/base/crashtests/394150-1.xhtml
deleted file mode 100644
index b2349c9f8b..0000000000
--- a/layout/base/crashtests/394150-1.xhtml
+++ /dev/null
@@ -1,27 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML">
-<head>
-<script>
-
-function boom()
-{
- var ms = document.createElementNS("http://www.w3.org/1998/Math/MathML", "ms");
- var textNode = document.getElementById("emptyset").firstChild;
- var mrow = document.getElementById("mrow");
-
- ms.appendChild(textNode); // *move* the text node from one place to another!
- mrow.appendChild(ms);
-}
-
-</script>
-</head>
-
-<body onload="boom();">
-
-<math xmlns="http://www.w3.org/1998/Math/MathML">
-<merror><emptyset id="emptyset">
- <mrow id="mrow"></mrow></emptyset></merror>
-</math>
-
-</body>
-
-</html>
diff --git a/layout/base/crashtests/399676-1.xhtml b/layout/base/crashtests/399676-1.xhtml
deleted file mode 100644
index 82b547e5ea..0000000000
--- a/layout/base/crashtests/399676-1.xhtml
+++ /dev/null
@@ -1,7 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML">
-<head>
-</head>
-<body>
-<math:mtd><span style="float: right;" /></math:mtd>
-</body>
-</html>
diff --git a/layout/base/crashtests/400445-1.xhtml b/layout/base/crashtests/400445-1.xhtml
deleted file mode 100644
index 9cb71dbbd6..0000000000
--- a/layout/base/crashtests/400445-1.xhtml
+++ /dev/null
@@ -1,22 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML">
-<head>
-<script>
-
-function boom()
-{
- var mtd1 = document.getElementById("mtd1");
- var mtd2 = document.getElementById("mtd2");
- var newSpan = document.createElementNS("http://www.w3.org/1999/xhtml", "span");
-
- mtd1.appendChild(newSpan);
- mtd1.removeAttribute("columnspan");
- mtd2.setAttribute("columnspan", 0);
-}
-
-</script>
-</head>
-
-<body onload="boom();">
-<math:mtd id="mtd1" columnspan="5" /><math:mtd id="mtd2" />
-</body>
-</html>
diff --git a/layout/base/crashtests/400904-1.xhtml b/layout/base/crashtests/400904-1.xhtml
deleted file mode 100644
index a00f42fd02..0000000000
--- a/layout/base/crashtests/400904-1.xhtml
+++ /dev/null
@@ -1,20 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML">
-<head>
-<script type="text/javascript">
-
-function boom()
-{
- var MATHML_NS = "http://www.w3.org/1998/Math/MathML";
- var mtd = document.getElementById("mtd");
- var n = document.createElementNS(MATHML_NS, 'mrow');
- mtd.appendChild(n);
- mtd.setAttribute('rowspan', 7);
-}
-
-</script>
-</head>
-
-<body onload="boom();">
-<math:mtd id="mtd"></math:mtd><math:mtr><math:mrow></math:mrow></math:mtr>
-</body>
-</html>
diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list
index 44572dad53..d9cf87f4d2 100644
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -7,7 +7,7 @@ load 56746-1.html
load 89101-1.html
load 89358-1.html
load 90205-1.html
-skip-if(cocoaWidget&&browserIsRemote) load 99776-1.html # Bug 849747
+skip-if(cocoaWidget) load 99776-1.html # Bug 849747
load 118931-1.html
load 121533-1.html
load 123049-1.html
@@ -31,7 +31,6 @@ load 234851-1.html
load 234851-2.html
load 241300-1.html
load 243159-1.html
-load 243159-2.xhtml
load 243519-1.html
load 244490-1.html
load 254367-1.html
@@ -116,7 +115,6 @@ load 350267-1.html
load 354133-1.html
load 354766-1.xhtml
load 355989-1.xhtml
-load 355993-1.xhtml
load chrome://reftest/content/crashtests/layout/base/crashtests/356325-1.xhtml
load 358729-1.xhtml
skip-if(Android) load chrome://reftest/content/crashtests/layout/base/crashtests/360339-1.xhtml
@@ -151,7 +149,6 @@ load 383129-1.html
load 384344-1.html
load 384392-1.xhtml
load 384392-2.svg
-load 384649-1.xhtml
load 385354.html
load 385866-1.xhtml
load 385880-1.xhtml
@@ -163,7 +160,6 @@ load 388715-1.html
load 390976-1.html
load 393661-1.html
load 393801-1.html
-load 394150-1.xhtml
load 397011-1.xhtml
load 398510-1.xhtml
load 398733-1.html
@@ -171,13 +167,10 @@ load 398733-2.html
load 399132-1.xhtml
load 399219-1.xhtml
load 399365-1.html
-load 399676-1.xhtml
load 399687-1.html
load 399940-1.xhtml
load 399951-1.html
load 399994-1.html
-load 400445-1.xhtml
-load 400904-1.xhtml
load 401734-1.html
load 401734-2.html
needs-focus pref(accessibility.browsewithcaret,true) load 403048.html
@@ -258,7 +251,7 @@ load 468645-3.xhtml
load 469861-1.xhtml
load 469861-2.xhtml
load 470851-1.xhtml
-asserts-if(Android&&!asyncPan,1-2) load 473042.xhtml # bug 1034369 (may also cause a few assertions to be registered on the next test)
+load 473042.xhtml # bug 1034369 (may also cause a few assertions to be registered on the next test)
asserts(1) load 474075.html # bug 1775003
load 477333-1.xhtml
load 477731-1.html
@@ -483,7 +476,6 @@ load 1428892.html
load 1429088.html
load 1429961.html
load 1429962.html
-load 1435015.html
load 1437155.html
load 1439016.html
load 1442018-1.html
diff --git a/layout/base/nsBidiPresUtils.cpp b/layout/base/nsBidiPresUtils.cpp
index b1215972c2..0331a7e5a0 100644
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -47,9 +47,10 @@ using BidiClass = intl::BidiClass;
using BidiDirection = intl::BidiDirection;
using BidiEmbeddingLevel = intl::BidiEmbeddingLevel;
-static const char16_t kSpace = 0x0020;
+static const char16_t kNextLine = 0x0085;
static const char16_t kZWSP = 0x200B;
static const char16_t kLineSeparator = 0x2028;
+static const char16_t kParagraphSeparator = 0x2029;
static const char16_t kObjectSubstitute = 0xFFFC;
static const char16_t kLRE = 0x202A;
static const char16_t kRLE = 0x202B;
@@ -60,11 +61,12 @@ static const char16_t kLRI = 0x2066;
static const char16_t kRLI = 0x2067;
static const char16_t kFSI = 0x2068;
static const char16_t kPDI = 0x2069;
-// All characters with Bidi type Segment Separator or Block Separator
+// All characters with Bidi type Segment Separator or Block Separator.
+// This should be kept in sync with the table in ReplaceSeparators.
static const char16_t kSeparators[] = {
- char16_t('\t'), char16_t('\r'), char16_t('\n'), char16_t(0xb),
- char16_t(0x1c), char16_t(0x1d), char16_t(0x1e), char16_t(0x1f),
- char16_t(0x85), char16_t(0x2029), char16_t(0)};
+ char16_t('\t'), char16_t('\r'), char16_t('\n'), char16_t(0xb),
+ char16_t(0x1c), char16_t(0x1d), char16_t(0x1e), char16_t(0x1f),
+ kNextLine, kParagraphSeparator, char16_t(0)};
#define NS_BIDI_CONTROL_FRAME ((nsIFrame*)0xfffb1d1)
@@ -478,115 +480,99 @@ struct MOZ_STACK_CLASS BidiParagraphData {
}
};
-struct MOZ_STACK_CLASS BidiLineData {
- AutoTArray<nsIFrame*, 16> mLogicalFrames;
- AutoTArray<nsIFrame*, 16> mVisualFrames;
- AutoTArray<int32_t, 16> mIndexMap;
- AutoTArray<BidiEmbeddingLevel, 16> mLevels;
- bool mIsReordered;
-
+class MOZ_STACK_CLASS BidiLineData {
+ public:
BidiLineData(nsIFrame* aFirstFrameOnLine, int32_t aNumFramesOnLine) {
- /**
- * Initialize the logically-ordered array of frames using the top-level
- * frames of a single line
- */
- bool isReordered = false;
- bool hasRTLFrames = false;
- bool hasVirtualControls = false;
-
+ // Initialize the logically-ordered array of frames using the top-level
+ // frames of a single line
auto appendFrame = [&](nsIFrame* frame, BidiEmbeddingLevel level) {
mLogicalFrames.AppendElement(frame);
mLevels.AppendElement(level);
mIndexMap.AppendElement(0);
- if (level.IsRTL()) {
- hasRTLFrames = true;
- }
};
- bool firstFrame = true;
for (nsIFrame* frame = aFirstFrameOnLine; frame && aNumFramesOnLine--;
frame = frame->GetNextSibling()) {
FrameBidiData bidiData = nsBidiPresUtils::GetFrameBidiData(frame);
- // Ignore virtual control before the first frame. Doing so should
- // not affect the visual result, but could avoid running into the
- // stripping code below for many cases.
- if (!firstFrame && bidiData.precedingControl != kBidiLevelNone) {
+ if (bidiData.precedingControl != kBidiLevelNone) {
appendFrame(NS_BIDI_CONTROL_FRAME, bidiData.precedingControl);
- hasVirtualControls = true;
}
appendFrame(frame, bidiData.embeddingLevel);
- firstFrame = false;
}
// Reorder the line
- BidiEngine::ReorderVisual(mLevels.Elements(), FrameCount(),
+ BidiEngine::ReorderVisual(mLevels.Elements(), mLevels.Length(),
mIndexMap.Elements());
- // Strip virtual frames
- if (hasVirtualControls) {
- auto originalCount = mLogicalFrames.Length();
- AutoTArray<int32_t, 16> realFrameMap;
- realFrameMap.SetCapacity(originalCount);
- size_t count = 0;
- for (auto i : IntegerRange(originalCount)) {
- if (mLogicalFrames[i] == NS_BIDI_CONTROL_FRAME) {
- realFrameMap.AppendElement(-1);
- } else {
- mLogicalFrames[count] = mLogicalFrames[i];
- mLevels[count] = mLevels[i];
- realFrameMap.AppendElement(count);
- count++;
- }
+ // Collect the frames in visual order, omitting virtual controls
+ // and noting whether frames are reordered.
+ for (uint32_t i = 0; i < mIndexMap.Length(); i++) {
+ nsIFrame* frame = mLogicalFrames[mIndexMap[i]];
+ if (frame == NS_BIDI_CONTROL_FRAME) {
+ continue;
}
- // Only keep index map for real frames.
- for (size_t i = 0, j = 0; i < originalCount; ++i) {
- auto newIndex = realFrameMap[mIndexMap[i]];
- if (newIndex != -1) {
- mIndexMap[j] = newIndex;
- j++;
- }
+ mVisualFrameIndex.AppendElement(mIndexMap[i]);
+ if (int32_t(i) != mIndexMap[i]) {
+ mIsReordered = true;
}
- mLogicalFrames.TruncateLength(count);
- mLevels.TruncateLength(count);
- mIndexMap.TruncateLength(count);
}
+ }
- for (int32_t i = 0; i < FrameCount(); i++) {
- mVisualFrames.AppendElement(LogicalFrameAt(mIndexMap[i]));
- if (i != mIndexMap[i]) {
- isReordered = true;
- }
- }
+ uint32_t LogicalFrameCount() const { return mLogicalFrames.Length(); }
+ uint32_t VisualFrameCount() const { return mVisualFrameIndex.Length(); }
- // If there's an RTL frame, assume the line is reordered
- mIsReordered = isReordered || hasRTLFrames;
+ nsIFrame* LogicalFrameAt(uint32_t aIndex) const {
+ return mLogicalFrames[aIndex];
}
- int32_t FrameCount() const { return mLogicalFrames.Length(); }
+ nsIFrame* VisualFrameAt(uint32_t aIndex) const {
+ return mLogicalFrames[mVisualFrameIndex[aIndex]];
+ }
- nsIFrame* LogicalFrameAt(int32_t aIndex) const {
- return mLogicalFrames[aIndex];
+ std::pair<nsIFrame*, BidiEmbeddingLevel> VisualFrameAndLevelAt(
+ uint32_t aIndex) const {
+ int32_t index = mVisualFrameIndex[aIndex];
+ return std::pair(mLogicalFrames[index], mLevels[index]);
}
- nsIFrame* VisualFrameAt(int32_t aIndex) const {
- return mVisualFrames[aIndex];
+ bool IsReordered() const { return mIsReordered; }
+
+ void InitContinuationStates(nsContinuationStates* aContinuationStates) const {
+ for (auto* frame : mLogicalFrames) {
+ if (frame != NS_BIDI_CONTROL_FRAME) {
+ nsBidiPresUtils::InitContinuationStates(frame, aContinuationStates);
+ }
+ }
}
+
+ private:
+ AutoTArray<nsIFrame*, 16> mLogicalFrames;
+ AutoTArray<int32_t, 16> mVisualFrameIndex;
+ AutoTArray<int32_t, 16> mIndexMap;
+ AutoTArray<BidiEmbeddingLevel, 16> mLevels;
+ bool mIsReordered = false;
};
#ifdef DEBUG
extern "C" {
-void MOZ_EXPORT DumpFrameArray(const nsTArray<nsIFrame*>& aFrames) {
- for (nsIFrame* frame : aFrames) {
+void MOZ_EXPORT DumpBidiLine(BidiLineData* aData, bool aVisualOrder) {
+ auto dump = [](nsIFrame* frame) {
if (frame == NS_BIDI_CONTROL_FRAME) {
fprintf_stderr(stderr, "(Bidi control frame)\n");
} else {
frame->List();
}
- }
-}
+ };
-void MOZ_EXPORT DumpBidiLine(BidiLineData* aData, bool aVisualOrder) {
- DumpFrameArray(aVisualOrder ? aData->mVisualFrames : aData->mLogicalFrames);
+ if (aVisualOrder) {
+ for (uint32_t i = 0; i < aData->VisualFrameCount(); i++) {
+ dump(aData->VisualFrameAt(i));
+ }
+ } else {
+ for (uint32_t i = 0; i < aData->LogicalFrameCount(); i++) {
+ dump(aData->LogicalFrameAt(i));
+ }
+ }
}
}
#endif
@@ -870,11 +856,32 @@ nsresult nsBidiPresUtils::Resolve(nsBlockFrame* aBlockFrame) {
return ResolveParagraph(&bpd);
}
+// In ResolveParagraph, we previously used ReplaceChar(kSeparators, kSpace)
+// to convert separators to spaces, but this hard-coded implementation is
+// substantially faster than the general-purpose ReplaceChar function.
+// This must be kept in sync with the definition of kSeparators.
+static inline void ReplaceSeparators(nsString& aText, size_t aStartIndex = 0) {
+ for (char16_t* cp = aText.BeginWriting() + aStartIndex;
+ cp < aText.EndWriting(); cp++) {
+ if (MOZ_UNLIKELY(*cp < char16_t(' '))) {
+ static constexpr char16_t SeparatorToSpace[32] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ' ', ' ',
+ ' ', 0x0c, ' ', 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, ' ', ' ', ' ', ' ',
+ };
+ *cp = SeparatorToSpace[*cp];
+ } else if (MOZ_UNLIKELY(*cp == kNextLine || *cp == kParagraphSeparator)) {
+ *cp = ' ';
+ }
+ }
+}
+
nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
if (aBpd->BufferLength() < 1) {
return NS_OK;
}
- aBpd->mBuffer.ReplaceChar(kSeparators, kSpace);
+
+ ReplaceSeparators(aBpd->mBuffer);
int32_t runCount;
@@ -1506,8 +1513,19 @@ nscoord nsBidiPresUtils::ReorderFrames(nsIFrame* aFirstFrameOnLine,
aStart = 0;
}
+ // No need to bidi-reorder the line if there's only a single frame.
+ if (aNumFramesOnLine == 1) {
+ auto bidiData = nsBidiPresUtils::GetFrameBidiData(aFirstFrameOnLine);
+ nsContinuationStates continuationStates;
+ InitContinuationStates(aFirstFrameOnLine, &continuationStates);
+ return aStart + RepositionFrame(aFirstFrameOnLine,
+ bidiData.embeddingLevel.IsLTR(), aStart,
+ &continuationStates, aLineWM, false,
+ containerSize);
+ }
+
BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
- return RepositionInlineFrames(&bld, aLineWM, containerSize, aStart);
+ return RepositionInlineFrames(bld, aLineWM, containerSize, aStart);
}
nsIFrame* nsBidiPresUtils::GetFirstLeaf(nsIFrame* aFrame) {
@@ -1857,40 +1875,30 @@ void nsBidiPresUtils::InitContinuationStates(
}
/* static */
-nscoord nsBidiPresUtils::RepositionInlineFrames(BidiLineData* aBld,
+nscoord nsBidiPresUtils::RepositionInlineFrames(const BidiLineData& aBld,
WritingMode aLineWM,
const nsSize& aContainerSize,
nscoord aStart) {
- nscoord start = aStart;
- nsIFrame* frame;
- int32_t count = aBld->mVisualFrames.Length();
- int32_t index;
nsContinuationStates continuationStates;
+ aBld.InitContinuationStates(&continuationStates);
- // Initialize continuation states to (nullptr, 0) for
- // each frame on the line.
- for (index = 0; index < count; index++) {
- InitContinuationStates(aBld->VisualFrameAt(index), &continuationStates);
- }
-
- // Reposition frames in visual order
- int32_t step, limit;
if (aLineWM.IsBidiLTR()) {
- index = 0;
- step = 1;
- limit = count;
+ for (auto index : IntegerRange(aBld.VisualFrameCount())) {
+ auto [frame, level] = aBld.VisualFrameAndLevelAt(index);
+ aStart +=
+ RepositionFrame(frame, level.IsLTR(), aStart, &continuationStates,
+ aLineWM, false, aContainerSize);
+ }
} else {
- index = count - 1;
- step = -1;
- limit = -1;
- }
- for (; index != limit; index += step) {
- frame = aBld->VisualFrameAt(index);
- start += RepositionFrame(
- frame, !(aBld->mLevels[aBld->mIndexMap[index]].IsRTL()), start,
- &continuationStates, aLineWM, false, aContainerSize);
+ for (auto index : Reversed(IntegerRange(aBld.VisualFrameCount()))) {
+ auto [frame, level] = aBld.VisualFrameAndLevelAt(index);
+ aStart +=
+ RepositionFrame(frame, level.IsLTR(), aStart, &continuationStates,
+ aLineWM, false, aContainerSize);
+ }
}
- return start;
+
+ return aStart;
}
bool nsBidiPresUtils::CheckLineOrder(nsIFrame* aFirstFrameOnLine,
@@ -1898,16 +1906,15 @@ bool nsBidiPresUtils::CheckLineOrder(nsIFrame* aFirstFrameOnLine,
nsIFrame** aFirstVisual,
nsIFrame** aLastVisual) {
BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
- int32_t count = bld.FrameCount();
if (aFirstVisual) {
*aFirstVisual = bld.VisualFrameAt(0);
}
if (aLastVisual) {
- *aLastVisual = bld.VisualFrameAt(count - 1);
+ *aLastVisual = bld.VisualFrameAt(bld.VisualFrameCount() - 1);
}
- return bld.mIsReordered;
+ return bld.IsReordered();
}
nsIFrame* nsBidiPresUtils::GetFrameToRightOf(const nsIFrame* aFrame,
@@ -1915,9 +1922,11 @@ nsIFrame* nsBidiPresUtils::GetFrameToRightOf(const nsIFrame* aFrame,
int32_t aNumFramesOnLine) {
BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
- int32_t count = bld.mVisualFrames.Length();
+ int32_t count = bld.VisualFrameCount();
- if (aFrame == nullptr && count) return bld.VisualFrameAt(0);
+ if (!aFrame && count) {
+ return bld.VisualFrameAt(0);
+ }
for (int32_t i = 0; i < count - 1; i++) {
if (bld.VisualFrameAt(i) == aFrame) {
@@ -1933,9 +1942,11 @@ nsIFrame* nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame* aFrame,
int32_t aNumFramesOnLine) {
BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
- int32_t count = bld.mVisualFrames.Length();
+ int32_t count = bld.VisualFrameCount();
- if (aFrame == nullptr && count) return bld.VisualFrameAt(count - 1);
+ if (!aFrame && count) {
+ return bld.VisualFrameAt(count - 1);
+ }
for (int32_t i = 1; i < count; i++) {
if (bld.VisualFrameAt(i) == aFrame) {
@@ -2476,9 +2487,19 @@ nsresult nsBidiPresUtils::ProcessTextForRenderingContext(
nsIRenderingContextBidiProcessor processor(&aRenderingContext,
aTextRunConstructionDrawTarget,
&aFontMetrics, nsPoint(aX, aY));
- nsAutoString text(aText, aLength);
- text.ReplaceChar(kSeparators, ' ');
- return ProcessText(text.BeginReading(), text.Length(), aBaseLevel,
+ nsDependentSubstring text(aText, aLength);
+ auto separatorIndex = text.FindCharInSet(kSeparators);
+ if (separatorIndex == kNotFound) {
+ return ProcessText(text.BeginReading(), text.Length(), aBaseLevel,
+ aPresContext, processor, aMode, aPosResolve,
+ aPosResolveCount, aWidth, aPresContext->BidiEngine());
+ }
+
+ // We need to replace any block or segment separators with space for bidi
+ // processing, so make a local copy.
+ nsAutoString localText(text);
+ ReplaceSeparators(localText, separatorIndex);
+ return ProcessText(localText.BeginReading(), localText.Length(), aBaseLevel,
aPresContext, processor, aMode, aPosResolve,
aPosResolveCount, aWidth, aPresContext->BidiEngine());
}
diff --git a/layout/base/nsBidiPresUtils.h b/layout/base/nsBidiPresUtils.h
index c369a06f2d..80cf8fbc41 100644
--- a/layout/base/nsBidiPresUtils.h
+++ b/layout/base/nsBidiPresUtils.h
@@ -21,7 +21,7 @@
#endif
struct BidiParagraphData;
-struct BidiLineData;
+class BidiLineData;
class gfxContext;
class nsFontMetrics;
class nsIFrame;
@@ -398,6 +398,8 @@ class nsBidiPresUtils {
mozilla::ComputedStyle* aComputedStyle);
private:
+ friend class BidiLineData;
+
static nsresult ProcessTextForRenderingContext(
const char16_t* aText, int32_t aLength,
mozilla::intl::BidiEmbeddingLevel aBaseLevel, nsPresContext* aPresContext,
@@ -536,7 +538,7 @@ class nsBidiPresUtils {
*
* @lina 04/11/2000
*/
- static nscoord RepositionInlineFrames(BidiLineData* aBld,
+ static nscoord RepositionInlineFrames(const BidiLineData& aBld,
mozilla::WritingMode aLineWM,
const nsSize& aContainerSize,
nscoord aStart);
diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp
index 3c1896c162..149f2f24bf 100644
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -170,6 +170,7 @@ nsIFrame* NS_NewSVGFEImageFrame(PresShell* aPresShell, ComputedStyle* aStyle);
nsIFrame* NS_NewSVGFEUnstyledLeafFrame(PresShell* aPresShell,
ComputedStyle* aStyle);
nsIFrame* NS_NewFileControlLabelFrame(PresShell*, ComputedStyle*);
+nsIFrame* NS_NewComboboxLabelFrame(PresShell*, ComputedStyle*);
nsIFrame* NS_NewMiddleCroppingLabelFrame(PresShell*, ComputedStyle*);
#include "mozilla/dom/NodeInfo.h"
@@ -320,7 +321,7 @@ static bool ShouldSuppressColumnSpanDescendants(nsIFrame* aFrame) {
}
if (!aFrame->IsBlockFrameOrSubclass() ||
- aFrame->HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS | NS_FRAME_OUT_OF_FLOW) ||
+ aFrame->HasAnyStateBits(NS_BLOCK_BFC | NS_FRAME_OUT_OF_FLOW) ||
aFrame->IsFixedPosContainingBlock()) {
// Need to suppress column-span if we:
// - Are a different block formatting context,
@@ -2286,7 +2287,7 @@ nsIFrame* nsCSSFrameConstructor::ConstructTableCell(
aState, content, innerPseudoStyle, cellFrame,
PseudoStyleType::scrolledContent, false, scrollFrame);
}
- cellInnerFrame = NS_NewBlockFormattingContext(mPresShell, innerPseudoStyle);
+ cellInnerFrame = NS_NewBlockFrame(mPresShell, innerPseudoStyle);
}
auto* parent = scrollFrame ? scrollFrame : cellFrame;
InitAndRestoreFrame(aState, content, parent, cellInnerFrame);
@@ -2566,7 +2567,7 @@ nsIFrame* nsCSSFrameConstructor::ConstructDocElementFrame(
MOZ_ASSERT(display->mDisplay == StyleDisplay::Block ||
display->mDisplay == StyleDisplay::FlowRoot,
"Unhandled display type for root element");
- contentFrame = NS_NewBlockFormattingContext(mPresShell, computedStyle);
+ contentFrame = NS_NewBlockFrame(mPresShell, computedStyle);
ConstructBlock(
state, aDocElement,
state.GetGeometricParent(*display, mDocElementContainingBlock),
@@ -3012,91 +3013,33 @@ static inline void ClearLazyBits(nsIContent* aStartContent,
}
}
-nsIFrame* nsCSSFrameConstructor::ConstructSelectFrame(
+/* static */
+const nsCSSFrameConstructor::FrameConstructionData*
+nsCSSFrameConstructor::FindSelectData(const Element& aElement,
+ ComputedStyle& aStyle) {
+ // Construct a frame-based listbox or combobox
+ const auto* sel = dom::HTMLSelectElement::FromNode(aElement);
+ MOZ_ASSERT(sel);
+ if (sel->IsCombobox()) {
+ static constexpr FrameConstructionData sComboboxData{
+ ToCreationFunc(NS_NewComboboxControlFrame), 0,
+ PseudoStyleType::buttonContent};
+ return &sComboboxData;
+ }
+ // FIXME: Can we simplify this to avoid needing ConstructListboxSelectFrame,
+ // and reuse ConstructScrollableBlock or so?
+ static constexpr FrameConstructionData sListBoxData{
+ &nsCSSFrameConstructor::ConstructListBoxSelectFrame};
+ return &sListBoxData;
+}
+
+nsIFrame* nsCSSFrameConstructor::ConstructListBoxSelectFrame(
nsFrameConstructorState& aState, FrameConstructionItem& aItem,
nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
nsFrameList& aFrameList) {
nsIContent* const content = aItem.mContent;
ComputedStyle* const computedStyle = aItem.mComputedStyle;
- // Construct a frame-based listbox or combobox
- dom::HTMLSelectElement* sel = dom::HTMLSelectElement::FromNode(content);
- MOZ_ASSERT(sel);
- if (sel->IsCombobox()) {
- // Construct a frame-based combo box.
- // The frame-based combo box is built out of three parts. A display area, a
- // button and a dropdown list. The display area and button are created
- // through anonymous content. The drop-down list's frame is created
- // explicitly. The combobox frame shares its content with the drop-down
- // list.
- nsComboboxControlFrame* comboboxFrame =
- NS_NewComboboxControlFrame(mPresShell, computedStyle);
-
- // Save the history state so we don't restore during construction
- // since the complete tree is required before we restore.
- nsILayoutHistoryState* historyState = aState.mFrameState;
- aState.mFrameState = nullptr;
- // Initialize the combobox frame
- InitAndRestoreFrame(aState, content,
- aState.GetGeometricParent(*aStyleDisplay, aParentFrame),
- comboboxFrame);
-
- comboboxFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
-
- aState.AddChild(comboboxFrame, aFrameList, content, aParentFrame);
-
- // Resolve pseudo element style for the dropdown list
- RefPtr<ComputedStyle> listStyle =
- mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
- PseudoStyleType::dropDownList, computedStyle);
-
- // child frames of combobox frame
- nsFrameList childList;
-
- // Create display and button frames from the combobox's anonymous content.
- // The anonymous content is appended to existing anonymous content for this
- // element (the scrollbars).
- //
- // nsComboboxControlFrame needs special frame creation behavior for its
- // first piece of anonymous content, which means that we can't take the
- // normal ProcessChildren path.
- AutoTArray<nsIAnonymousContentCreator::ContentInfo, 2> newAnonymousItems;
- DebugOnly<nsresult> rv =
- GetAnonymousContent(content, comboboxFrame, newAnonymousItems);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- MOZ_ASSERT(!newAnonymousItems.IsEmpty());
-
- // Manually create a frame for the special NAC.
- MOZ_ASSERT(newAnonymousItems[0].mContent ==
- comboboxFrame->GetDisplayNode());
- newAnonymousItems.RemoveElementAt(0);
- nsIFrame* customFrame = comboboxFrame->CreateFrameForDisplayNode();
- MOZ_ASSERT(customFrame);
- childList.AppendFrame(nullptr, customFrame);
-
- nsFrameConstructorSaveState floatSaveState;
- aState.MaybePushFloatContainingBlock(comboboxFrame, floatSaveState);
-
- // The other piece of NAC can take the normal path.
- AutoFrameConstructionItemList fcItems(this);
- AutoFrameConstructionPageName pageNameTracker(aState, comboboxFrame);
- AddFCItemsForAnonymousContent(aState, comboboxFrame, newAnonymousItems,
- fcItems, pageNameTracker);
- ConstructFramesFromItemList(aState, fcItems, comboboxFrame,
- /* aParentIsWrapperAnonBox = */ false,
- childList);
-
- comboboxFrame->SetInitialChildList(FrameChildListID::Principal,
- std::move(childList));
-
- aState.mFrameState = historyState;
- if (aState.mFrameState) {
- // Restore frame state for the entire subtree of |comboboxFrame|.
- RestoreFrameState(comboboxFrame, aState.mFrameState);
- }
- return comboboxFrame;
- }
-
// Listbox, not combobox
nsContainerFrame* listFrame =
NS_NewListControlFrame(mPresShell, computedStyle);
@@ -3175,7 +3118,7 @@ nsIFrame* nsCSSFrameConstructor::ConstructFieldSetFrame(
const nsStyleDisplay* fieldsetContentDisplay =
fieldsetContentStyle->StyleDisplay();
- bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow();
+ const bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow();
nsContainerFrame* scrollFrame = nullptr;
if (isScrollable) {
fieldsetContentStyle = BeginBuildingScrollFrame(
@@ -3204,8 +3147,7 @@ nsIFrame* nsCSSFrameConstructor::ConstructFieldSetFrame(
MOZ_ASSERT(fieldsetContentDisplay->mDisplay == StyleDisplay::Block,
"bug in StyleAdjuster::adjust_for_fieldset_content?");
- contentFrame =
- NS_NewBlockFormattingContext(mPresShell, fieldsetContentStyle);
+ contentFrame = NS_NewBlockFrame(mPresShell, fieldsetContentStyle);
if (fieldsetContentStyle->StyleColumn()->IsColumnContainerStyle()) {
contentFrameTop = BeginBuildingColumns(
aState, content, parent, contentFrame, fieldsetContentStyle);
@@ -3499,11 +3441,18 @@ nsCSSFrameConstructor::FindHTMLData(const Element& aElement,
"Unexpected parent for fieldset content anon box");
if (aElement.IsInNativeAnonymousSubtree() &&
- aElement.NodeInfo()->NameAtom() == nsGkAtoms::label &&
- static_cast<nsFileControlFrame*>(do_QueryFrame(aParentFrame))) {
- static constexpr FrameConstructionData sFileLabelData(
- NS_NewFileControlLabelFrame);
- return &sFileLabelData;
+ aElement.NodeInfo()->NameAtom() == nsGkAtoms::label && aParentFrame) {
+ if (static_cast<nsFileControlFrame*>(do_QueryFrame(aParentFrame))) {
+ static constexpr FrameConstructionData sFileLabelData(
+ NS_NewFileControlLabelFrame);
+ return &sFileLabelData;
+ }
+ if (aParentFrame->GetParent() &&
+ aParentFrame->GetParent()->IsComboboxControlFrame()) {
+ static constexpr FrameConstructionData sComboboxLabelData(
+ NS_NewComboboxLabelFrame);
+ return &sComboboxLabelData;
+ }
}
static constexpr FrameConstructionDataByTag sHTMLData[] = {
@@ -3515,7 +3464,7 @@ nsCSSFrameConstructor::FindHTMLData(const Element& aElement,
SIMPLE_TAG_CREATE(wbr, NS_NewWBRFrame),
SIMPLE_TAG_CHAIN(input, nsCSSFrameConstructor::FindInputData),
SIMPLE_TAG_CREATE(textarea, NS_NewTextControlFrame),
- COMPLEX_TAG_CREATE(select, &nsCSSFrameConstructor::ConstructSelectFrame),
+ SIMPLE_TAG_CHAIN(select, nsCSSFrameConstructor::FindSelectData),
SIMPLE_TAG_CHAIN(object, nsCSSFrameConstructor::FindObjectData),
SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData),
COMPLEX_TAG_CREATE(fieldset,
@@ -3741,11 +3690,6 @@ void nsCSSFrameConstructor::ConstructFrameFromItemInternal(
CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS,
FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
#undef CHECK_ONLY_ONE_BIT
- NS_ASSERTION(!(bits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) ||
- ((bits & FCDATA_FUNC_IS_FULL_CTOR) &&
- data->mFunc.mFullConstructor ==
- &nsCSSFrameConstructor::ConstructNonScrollableBlock),
- "Unexpected FCDATA_FORCED_NON_SCROLLABLE_BLOCK flag");
MOZ_ASSERT(
!(bits & FCDATA_IS_WRAPPER_ANON_BOX) || (bits & FCDATA_USE_CHILD_ITEMS),
"Wrapper anon boxes should always have FCDATA_USE_CHILD_ITEMS");
@@ -3823,7 +3767,7 @@ void nsCSSFrameConstructor::ConstructFrameFromItemInternal(
innerFrame = outerFrame;
break;
default: {
- innerFrame = NS_NewBlockFormattingContext(mPresShell, outerStyle);
+ innerFrame = NS_NewBlockFrame(mPresShell, outerStyle);
if (outerStyle->StyleColumn()->IsColumnContainerStyle()) {
outerFrame = BeginBuildingColumns(aState, content, container,
innerFrame, outerStyle);
@@ -3836,7 +3780,7 @@ void nsCSSFrameConstructor::ConstructFrameFromItemInternal(
}
}
} else {
- innerFrame = NS_NewBlockFormattingContext(mPresShell, outerStyle);
+ innerFrame = NS_NewBlockFrame(mPresShell, outerStyle);
InitAndRestoreFrame(aState, content, container, innerFrame);
outerFrame = innerFrame;
}
@@ -4386,14 +4330,14 @@ nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay& aDisplay,
// XXX Ignore tables for the time being (except caption)
const uint32_t kCaptionCtorFlags =
FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable);
- bool caption = aDisplay.mDisplay == StyleDisplay::TableCaption;
- bool suppressScrollFrame = false;
- bool needScrollFrame =
+ const bool caption = aDisplay.mDisplay == StyleDisplay::TableCaption;
+ const bool needScrollFrame =
aDisplay.IsScrollableOverflow() && !propagatedScrollToViewport;
if (needScrollFrame) {
- suppressScrollFrame = mPresShell->GetPresContext()->IsPaginated() &&
- aDisplay.IsBlockOutsideStyle() &&
- !aElement.IsInNativeAnonymousSubtree();
+ const bool suppressScrollFrame =
+ mPresShell->GetPresContext()->IsPaginated() &&
+ aDisplay.IsBlockOutsideStyle() &&
+ !aElement.IsInNativeAnonymousSubtree();
if (!suppressScrollFrame) {
static constexpr FrameConstructionData sScrollableBlockData[2] = {
{&nsCSSFrameConstructor::ConstructScrollableBlock},
@@ -4401,27 +4345,14 @@ nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay& aDisplay,
kCaptionCtorFlags}};
return &sScrollableBlockData[caption];
}
-
- // If the scrollable frame would have propagated its scrolling to the
- // viewport, we still want to construct a regular block rather than a
- // scrollframe so that it paginates correctly, but we don't want to set
- // the bit on the block that tells it to clip at paint time.
- if (mPresShell->GetPresContext()->ElementWouldPropagateScrollStyles(
- aElement)) {
- suppressScrollFrame = false;
- }
}
// Handle various non-scrollable blocks.
- static constexpr FrameConstructionData sNonScrollableBlockData[2][2] = {
- {{&nsCSSFrameConstructor::ConstructNonScrollableBlock},
- {&nsCSSFrameConstructor::ConstructNonScrollableBlock,
- kCaptionCtorFlags}},
- {{&nsCSSFrameConstructor::ConstructNonScrollableBlock,
- FCDATA_FORCED_NON_SCROLLABLE_BLOCK},
- {&nsCSSFrameConstructor::ConstructNonScrollableBlock,
- FCDATA_FORCED_NON_SCROLLABLE_BLOCK | kCaptionCtorFlags}}};
- return &sNonScrollableBlockData[suppressScrollFrame][caption];
+ static constexpr FrameConstructionData sNonScrollableBlockData[2] = {
+ {&nsCSSFrameConstructor::ConstructNonScrollableBlock},
+ {&nsCSSFrameConstructor::ConstructNonScrollableBlock,
+ kCaptionCtorFlags}};
+ return &sNonScrollableBlockData[caption];
}
case StyleDisplayInside::Table: {
static constexpr FrameConstructionData data(
@@ -4555,8 +4486,7 @@ nsIFrame* nsCSSFrameConstructor::ConstructScrollableBlock(
// Create our block frame
// pass a temporary stylecontext, the correct one will be set later
- nsContainerFrame* scrolledFrame =
- NS_NewBlockFormattingContext(mPresShell, computedStyle);
+ nsContainerFrame* scrolledFrame = NS_NewBlockFrame(mPresShell, computedStyle);
// Make sure to AddChild before we call ConstructBlock so that we
// end up before our descendants in fixed-pos lists as needed.
@@ -4579,26 +4509,7 @@ nsIFrame* nsCSSFrameConstructor::ConstructNonScrollableBlock(
nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
nsFrameList& aFrameList) {
ComputedStyle* const computedStyle = aItem.mComputedStyle;
-
- // We want a block formatting context root in paginated contexts for
- // every block that would be scrollable in a non-paginated context.
- // We mark our blocks with a bit here if this condition is true, so
- // we can check it later in nsIFrame::ApplyPaginatedOverflowClipping.
- bool clipPaginatedOverflow =
- (aItem.mFCData->mBits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) != 0;
- nsFrameState flags = nsFrameState(0);
- if ((aDisplay->IsAbsolutelyPositionedStyle() || aDisplay->IsFloatingStyle() ||
- aDisplay->DisplayInside() == StyleDisplayInside::FlowRoot ||
- clipPaginatedOverflow) &&
- !aParentFrame->IsInSVGTextSubtree()) {
- flags = NS_BLOCK_STATIC_BFC;
- if (clipPaginatedOverflow) {
- flags |= NS_BLOCK_CLIP_PAGINATED_OVERFLOW;
- }
- }
-
nsContainerFrame* newFrame = NS_NewBlockFrame(mPresShell, computedStyle);
- newFrame->AddStateBits(flags);
ConstructBlock(aState, aItem.mContent,
aState.GetGeometricParent(*aDisplay, aParentFrame),
aParentFrame, computedStyle, &newFrame, aFrameList,
@@ -5165,10 +5076,14 @@ static bool ShouldSuppressFrameInSelect(const nsIContent* aParent,
return false;
}
+ // Allow native anonymous content no matter what.
+ if (aChild.IsRootOfNativeAnonymousSubtree()) {
+ return false;
+ }
+
// Options with labels have their label text added in ::before by forms.css.
// Suppress frames for their child text.
- if (aParent->IsHTMLElement(nsGkAtoms::option) &&
- !aChild.IsRootOfNativeAnonymousSubtree()) {
+ if (aParent->IsHTMLElement(nsGkAtoms::option)) {
return aParent->AsElement()->HasNonEmptyAttr(nsGkAtoms::label);
}
@@ -5191,11 +5106,7 @@ static bool ShouldSuppressFrameInSelect(const nsIContent* aParent,
return false;
}
- // Allow native anonymous content no matter what.
- if (aChild.IsRootOfNativeAnonymousSubtree()) {
- return false;
- }
-
+ // Anything else is not ok.
return true;
}
@@ -8042,6 +7953,20 @@ nsIFrame* nsCSSFrameConstructor::CreateContinuingFrame(
return newFrame;
}
+void nsCSSFrameConstructor::MaybeSetNextPageContentFramePageName(
+ const nsIFrame* aFrame) {
+ MOZ_ASSERT(aFrame, "Frame should not be null");
+ // No parent means the root frame, which isn't what this funciton is for.
+ MOZ_ASSERT(aFrame->GetParent(),
+ "Frame should be the first child placed on a new page, not the "
+ "root frame.");
+ if (mNextPageContentFramePageName) {
+ return;
+ }
+ const nsAtom* const autoValue = aFrame->GetParent()->GetAutoPageValue();
+ mNextPageContentFramePageName = aFrame->ComputePageValue(autoValue);
+}
+
nsresult nsCSSFrameConstructor::ReplicateFixedFrames(
nsPageContentFrame* aParentFrame) {
// Now deal with fixed-pos things.... They should appear on all pages,
@@ -8743,15 +8668,14 @@ void nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems(
mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
PseudoStyleType::anonymousItem, aParentFrame->Style());
- static constexpr FrameConstructionData sBlockFormattingContextFCData(
- ToCreationFunc(NS_NewBlockFormattingContext),
- FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
- FCDATA_IS_WRAPPER_ANON_BOX);
+ static constexpr FrameConstructionData sBlockFCData(
+ ToCreationFunc(NS_NewBlockFrame), FCDATA_SKIP_FRAMESET |
+ FCDATA_USE_CHILD_ITEMS |
+ FCDATA_IS_WRAPPER_ANON_BOX);
- FrameConstructionItem* newItem = new (this)
- FrameConstructionItem(&sBlockFormattingContextFCData,
- // Use the content of our parent frame
- parentContent, wrapperStyle.forget(), true);
+ // Use the content of our parent frame
+ auto* newItem = new (this) FrameConstructionItem(
+ &sBlockFCData, parentContent, wrapperStyle.forget(), true);
newItem->mIsAllInline =
newItem->mComputedStyle->StyleDisplay()->IsInlineOutsideStyle();
diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h
index 1b48fd15dc..283d1385ce 100644
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -297,12 +297,39 @@ class nsCSSFrameConstructor final : public nsFrameManager {
nsContainerFrame* aParentFrame,
bool aIsFluid = true);
- void SetNextPageContentFramePageName(const nsAtom* aAtom) {
+ /**
+ * Sets the page name when a page break is being generated due to a change
+ * in page name.
+ *
+ * Should only be used during paginated reflow, to signal what page value
+ * the next page content frame should have.
+ *
+ * It is an error to set this if a new page name has already been set, either
+ * through SetNextPageContentFramePageName or
+ * MaybeSetNextPageContentFramePageName.
+ */
+ void SetNextPageContentFramePageName(const nsAtom* aPageName) {
+ MOZ_ASSERT(aPageName, "New page name should never be null");
MOZ_ASSERT(!mNextPageContentFramePageName,
"PageContentFrame page name was already set");
- mNextPageContentFramePageName = aAtom;
+ mNextPageContentFramePageName = aPageName;
}
+ /**
+ * If a new page name has not been set for the next page, sets the value
+ * using the given frame.
+ *
+ * |aFrame| should be a frame to be placed on the new page.
+ *
+ * This function handles the work of resolving an atom for the frame, and
+ * avoids doing this extra work when not necessary.
+ *
+ * This is used during block reflow when a page break has occurred but it was
+ * not caused by a change in page name. It should only be used during
+ * paginated reflow.
+ */
+ void MaybeSetNextPageContentFramePageName(const nsIFrame* aFrame);
+
// Copy over fixed frames from aParentFrame's prev-in-flow
nsresult ReplicateFixedFrames(nsPageContentFrame* aParentFrame);
@@ -696,10 +723,6 @@ class nsCSSFrameConstructor final : public nsFrameManager {
This can be used with or without FCDATA_FUNC_IS_FULL_CTOR.
The child items might still need table pseudo processing. */
#define FCDATA_USE_CHILD_ITEMS 0x10000
- /* If FCDATA_FORCED_NON_SCROLLABLE_BLOCK is set, then this block
- would have been scrollable but has been forced to be
- non-scrollable due to being in a paginated context. */
-#define FCDATA_FORCED_NON_SCROLLABLE_BLOCK 0x20000
/* If FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS is set, then create a
block formatting context wrapper around the kids of this frame
using the FrameConstructionData's mPseudoAtom for its anonymous
@@ -1361,14 +1384,6 @@ class nsCSSFrameConstructor final : public nsFrameManager {
nsFrameState aTypeBit);
private:
- // ConstructSelectFrame puts the new frame in aFrameList and
- // handles the kids of the select.
- nsIFrame* ConstructSelectFrame(nsFrameConstructorState& aState,
- FrameConstructionItem& aItem,
- nsContainerFrame* aParentFrame,
- const nsStyleDisplay* aStyleDisplay,
- nsFrameList& aFrameList);
-
// ConstructFieldSetFrame puts the new frame in aFrameList and
// handles the kids of the fieldset
nsIFrame* ConstructFieldSetFrame(nsFrameConstructorState& aState,
@@ -1377,6 +1392,12 @@ class nsCSSFrameConstructor final : public nsFrameManager {
const nsStyleDisplay* aStyleDisplay,
nsFrameList& aFrameList);
+ nsIFrame* ConstructListBoxSelectFrame(nsFrameConstructorState& aState,
+ FrameConstructionItem& aItem,
+ nsContainerFrame* aParentFrame,
+ const nsStyleDisplay* aStyleDisplay,
+ nsFrameList& aFrameList);
+
// Creates a block frame wrapping an anonymous ruby frame.
nsIFrame* ConstructBlockRubyFrame(nsFrameConstructorState& aState,
FrameConstructionItem& aItem,
@@ -1423,6 +1444,8 @@ class nsCSSFrameConstructor final : public nsFrameManager {
nsIFrame* aParentFrame,
ComputedStyle&);
// HTML data-finding helper functions
+ static const FrameConstructionData* FindSelectData(const Element&,
+ ComputedStyle&);
static const FrameConstructionData* FindImgData(const Element&,
ComputedStyle&);
static const FrameConstructionData* FindGeneratedImageData(const Element&,
diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp
index ab61daae87..d1cf9bf237 100644
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -1143,6 +1143,8 @@ nsDocumentViewer::PermitUnload(PermitUnloadAction aAction,
*aPermitUnload = true;
+ NS_ENSURE_STATE(mContainer);
+
RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext();
if (!bc) {
return NS_OK;
@@ -1224,7 +1226,7 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY PermitUnloadResult
nsDocumentViewer::DispatchBeforeUnload() {
AutoDontWarnAboutSyncXHR disableSyncXHRWarning;
- if (!mDocument || mInPermitUnload || mInPermitUnloadPrompt) {
+ if (!mDocument || mInPermitUnload || mInPermitUnloadPrompt || !mContainer) {
return eAllowNavigation;
}
@@ -2546,6 +2548,8 @@ nsDocumentViewer::ForgetReloadEncoding() {
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP nsDocumentViewer::GetContentSize(
int32_t aMaxWidth, int32_t aMaxHeight, int32_t aPrefWidth, int32_t* aWidth,
int32_t* aHeight) {
+ NS_ENSURE_STATE(mContainer);
+
RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext();
NS_ENSURE_TRUE(bc, NS_ERROR_NOT_AVAILABLE);
diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp
index 28230421d5..db766f6603 100644
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -9492,8 +9492,9 @@ nsRect nsLayoutUtils::ComputeSVGOriginBox(SVGViewportElement* aElement) {
}
/* static */
-nsRect nsLayoutUtils::ComputeSVGReferenceRect(nsIFrame* aFrame,
- StyleGeometryBox aGeometryBox) {
+nsRect nsLayoutUtils::ComputeSVGReferenceRect(
+ nsIFrame* aFrame, StyleGeometryBox aGeometryBox,
+ MayHaveNonScalingStrokeCyclicDependency aMayHaveCyclicDependency) {
MOZ_ASSERT(aFrame->GetContent()->IsSVGElement());
nsRect r;
@@ -9502,9 +9503,12 @@ nsRect nsLayoutUtils::ComputeSVGReferenceRect(nsIFrame* aFrame,
// XXX Bug 1299876
// The size of stroke-box is not correct if this graphic element has
// specific stroke-linejoin or stroke-linecap.
- gfxRect bbox =
- SVGUtils::GetBBox(aFrame, SVGUtils::eBBoxIncludeFillGeometry |
- SVGUtils::eBBoxIncludeStroke);
+ const uint32_t flags = SVGUtils::eBBoxIncludeFillGeometry |
+ SVGUtils::eBBoxIncludeStroke |
+ (bool(aMayHaveCyclicDependency)
+ ? SVGUtils::eAvoidCycleIfNonScalingStroke
+ : 0);
+ gfxRect bbox = SVGUtils::GetBBox(aFrame, flags);
r = nsLayoutUtils::RoundGfxRectToAppRect(bbox, AppUnitsPerCSSPixel());
break;
}
diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h
index 3cd01dce9f..697b139ed9 100644
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2913,7 +2913,13 @@ class nsLayoutUtils {
// Compute the geometry box for SVG layout. The caller should map the CSS box
// into the proper SVG box.
- static nsRect ComputeSVGReferenceRect(nsIFrame*, StyleGeometryBox);
+ // |aMayHaveCyclicDependency| is used for stroke-box to avoid the cyclic
+ // dependency if any of its descendants uses non-scaling-stroke.
+ enum class MayHaveNonScalingStrokeCyclicDependency : bool { No, Yes };
+ static nsRect ComputeSVGReferenceRect(
+ nsIFrame*, StyleGeometryBox,
+ MayHaveNonScalingStrokeCyclicDependency =
+ MayHaveNonScalingStrokeCyclicDependency::No);
// Compute the geometry box for CSS layout. The caller should map the SVG box
// into the proper CSS box.
diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp
index c1b03aebf4..7d9515c495 100644
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -906,7 +906,6 @@ Maybe<ColorScheme> nsPresContext::GetOverriddenOrEmbedderColorScheme() const {
case dom::PrefersColorSchemeOverride::Light:
return Some(ColorScheme::Light);
case dom::PrefersColorSchemeOverride::None:
- case dom::PrefersColorSchemeOverride::EndGuard_:
break;
}
diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h
index bc4e70dbcf..563dac0eae 100644
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -1397,7 +1397,8 @@ class nsPresContext : public nsISupports, public mozilla::SupportsWeakPtr {
#ifdef DEBUG
private:
friend struct nsAutoLayoutPhase;
- mozilla::EnumeratedArray<nsLayoutPhase, nsLayoutPhase::COUNT, uint32_t>
+ mozilla::EnumeratedArray<nsLayoutPhase, uint32_t,
+ size_t(nsLayoutPhase::COUNT)>
mLayoutPhaseCount;
public:
diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp
index af780bb192..a5c2b1ded9 100644
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -2264,7 +2264,8 @@ void nsRefreshDriver::DetermineProximityToViewportAndNotifyResizeObservers() {
return false;
}
return ps->HasContentVisibilityAutoFrames() ||
- aDocument->HasResizeObservers();
+ aDocument->HasResizeObservers() ||
+ aDocument->HasElementsWithLastRememberedSize();
};
AutoTArray<RefPtr<Document>, 32> documents;
diff --git a/layout/base/tests/mochitest.toml b/layout/base/tests/mochitest.toml
index d4b7932cfc..24924809c0 100644
--- a/layout/base/tests/mochitest.toml
+++ b/layout/base/tests/mochitest.toml
@@ -234,6 +234,8 @@ support-files = ["bug1448730.html"]
["test_bug1756118.html"]
+["test_bug1836801.html"]
+
["test_caret_browsing_around_form_controls.html"]
skip-if = ["os == 'android'"]
diff --git a/layout/base/tests/test_bug1836801.html b/layout/base/tests/test_bug1836801.html
new file mode 100644
index 0000000000..4584394600
--- /dev/null
+++ b/layout/base/tests/test_bug1836801.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+// this is a crashtest, just getting to the end is a pass, fullscreen
+// doesn't work in crashtests though not sure why -->
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.requestFlakyTimeout("crashtest sensitive to timing");
+
+function begin() {
+ SimpleTest.waitForFocus(begin2);
+}
+
+function begin2() {
+ SpecialPowers.pushPrefEnv({
+ "set":[["full-screen-api.allow-trusted-requests-only", false]]
+ }, startTest);
+}
+
+window.addEventListener("load", begin);
+
+const func_0 = async function(arg0) {
+ //SpecialPowers.wrap(document).notifyUserGestureActivation();
+ await arg0.originalTarget.requestFullscreen()
+ arg0.originalTarget.ariaValueText = "a"
+}
+
+async function startTest() {
+ let a = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas")
+ document.documentElement.appendChild(a)
+ document.addEventListener("DOMAttrModified", func_0, { })
+ let b = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas")
+ document.documentElement.appendChild(b)
+ b.setAttribute("class", "x")
+ //SpecialPowers.wrap(document).notifyUserGestureActivation();
+ await b.mozRequestFullScreen()
+ let c = document.createElementNS("http://www.w3.org/1999/xhtml", "slot")
+ document.documentElement.appendChild(c)
+ c.setAttribute("class", "x")
+ b.inert = true
+ setTimeout(async () => {
+ b.setAttribute("title", "a")
+ }, 200)
+ a.ariaSort = "descending"
+ setTimeout(finishup, 400);
+}
+async function finishup() {
+ await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
+ await document.exitFullscreen();
+ SimpleTest.finish();
+}
+</script>
+</html>
diff --git a/layout/base/tests/test_event_target_iframe_oop.html b/layout/base/tests/test_event_target_iframe_oop.html
deleted file mode 100644
index 562433c955..0000000000
--- a/layout/base/tests/test_event_target_iframe_oop.html
+++ /dev/null
@@ -1,177 +0,0 @@
-<!DOCTYPE HTML>
-<html id="html" style="height:100%">
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=921928
--->
-<head>
- <title>Test for bug 921928</title>
- <script src="/tests/SimpleTest/SimpleTest.js"></script>
- <script src="/tests/SimpleTest/EventUtils.js"></script>
- <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
- <style>
- #dialer {
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
- height: 50px;
- background: green;
- }
-
- #apps {
- position: absolute;
- left: 0;
- top: 51px;
- width: 100%;
- height: 100px;
- background: blue;
- }
-
- .hit {
- position: absolute;
- width: 3px;
- height: 3px;
- z-index: 20;
- background: red;
- border: 1px solid red;
- }
- </style>
-</head>
-<body id="body" style="margin:0; width:100%; height:100%">
-<script type="application/javascript">
-SimpleTest.waitForExplicitFinish();
-
-var prefs = [
- ["ui.mouse.radius.enabled", true],
- ["ui.mouse.radius.inputSource.touchOnly", false],
- ["ui.mouse.radius.leftmm", 12],
- ["ui.mouse.radius.topmm", 8],
- ["ui.mouse.radius.rightmm", 4],
- ["ui.mouse.radius.bottommm", 4],
- ["ui.mouse.radius.visitedweight", 50],
-];
-
-var eventTarget;
-var debugHit = [];
-
-function endTest() {
- SimpleTest.finish();
- SpecialPowers.removePermission("browser", location.href);
- for (var pref in prefs) {
- SpecialPowers.pushPrefEnv({"clear": pref[0]}, function() {});
- }
-}
-
-function testMouseClick(idPosition, dx, dy, idTarget, msg, options) {
- eventTarget = null;
- synthesizeMouse(document.getElementById(idPosition), dx, dy, options || {});
- try {
- is(eventTarget.id, idTarget,
- "checking '" + idPosition + "' offset " + dx + "," + dy + " [" + msg + "]");
- } catch (ex) {
- ok(false, "checking '" + idPosition + "' offset " + dx + "," + dy + " [" + msg + "]; got " + eventTarget);
- }
-}
-
-function showDebug() {
- for (var i = 0; i < debugHit.length; i++) {
- document.body.appendChild(debugHit[i]);
- }
-
- var screenshot = SpecialPowers.snapshotWindow(window, true);
- dump('IMAGE:' + screenshot.toDataURL() + '\n');
-}
-
-/*
- Setup the test environment: enabling event fluffing (all ui.* preferences),
- and enabling remote process.
-*/
-function setupTest(cont) {
- SpecialPowers.addPermission("browser", true, document);
- SpecialPowers.pushPrefEnv({"set": prefs}, cont);
-}
-
-function execTest() {
- /*
- Creating two iframes that mimics the attention screen behavior on the
- device:
- - the 'dialer' iframe is the attention screen you have when a call is
- in place. it is a green bar, so we copy it as green here too
- - the 'apps' iframe mimics another application that is being run, be it
- dialer, sms, ..., anything that the user might want to trigger during
- a call
-
- The bug we intent to reproduce here is that in this case, if the user taps
- onto the top of the 'apps', the event fluffing code will in fact redirect
- the event to the 'dialer' iframe. In practice, this is bug 921928 where
- during a call the user wants to place a second call, and while typing the
- phone number, wants to tap onto the 'delete' key to erase a digit, but ends
- tapping and activating the dialer.
- */
- var dialer = document.createElement('iframe');
- dialer.id = 'dialer';
- dialer.src = '';
- // Force OOP
- dialer.setAttribute('mozbrowser', 'true');
- dialer.setAttribute('remote', 'true');
- document.body.appendChild(dialer);
-
- var apps = document.createElement('iframe');
- apps.id = 'apps';
- apps.src = 'bug921928_event_target_iframe_apps_oop.html';
- // Force OOP
- apps.setAttribute('mozbrowser', 'true');
- apps.setAttribute('remote', 'true');
- document.body.appendChild(apps);
-
- var handleEvent = function(event) {
- eventTarget = event.target;
-
- // We draw a small red div to show where the event has tapped
- var hit = document.createElement('div');
- hit.style.left = (event.clientX - 1.5) + 'px';
- hit.style.top = (event.clientY - 1.5) + 'px';
- hit.classList.add('hit');
- debugHit.push(hit);
- };
-
- // In real life, the 'dialer' has a 'mousedown', so we mimic one too,
- // to reproduce the same behavior
- dialer.addEventListener('mousedown', function(e) {});
-
- // This event listener is just here to record what iframe has been hit,
- // and sets the 'eventTarget' to the iframe's id value so that the
- // testMouseClick() code can correctly check. We cannot add it on the
- // 'apps' otherwise it will alter the behavior of the test.
- document.addEventListener('mousedown', handleEvent);
-
- // In the following, the coordinates are relative to the iframe
-
- // First, we check that tapping onto the 'dialer' correctly triggers the
- // dialer.
- testMouseClick("dialer", 20, 1, "dialer", "correct hit on dialer with mouse input");
- testMouseClick("dialer", 20, 1, "dialer", "correct hit on dialer with touch input", {
- inputSource: MouseEvent.MOZ_SOURCE_TOUCH
- });
-
- // Now this is it: we tap inside 'apps', but very close to the border between
- // 'apps' and 'dialer'. Without the fix from this bug, this test will fail.
- testMouseClick("apps", 20, 1, "apps", "apps <iframe mozbrowser remote> hit for mouse input");
- testMouseClick("apps", 20, 1, "apps", "apps <iframe mozbrowser remote> hit for touch input", {
- inputSource: MouseEvent.MOZ_SOURCE_TOUCH
- });
-
- // Show small red spots of where the click happened
- // showDebug();
-
- endTest();
-}
-
-function runTest() {
- setupTest(execTest);
-}
-
-addEventListener('load', function() { SimpleTest.executeSoon(runTest); });
-</script>
-</body>
-</html>