From d22246efadb462d21b3cfc87900dd15e3dbce881 Mon Sep 17 00:00:00 2001 From: Cameron Kaiser Date: Sat, 1 Jul 2017 12:15:04 -0700 Subject: [PATCH] #409, #411: M1371620 M1367906 M1366218+backbugs M1358453 M1366250 --- dom/base/nsFocusManager.cpp | 2 + dom/base/nsFocusManager.h | 28 ++++++++++ dom/html/HTMLInputElement.cpp | 12 +++++ dom/html/nsGenericHTMLElement.cpp | 7 ++- dom/html/nsTextEditorState.cpp | 17 ++++++ dom/html/nsTextEditorState.h | 5 ++ layout/base/nsPresShell.cpp | 7 +++ layout/generic/nsFrame.cpp | 16 ++++-- layout/generic/nsIFrame.h | 2 + toolkit/content/widgets/scrollbox.xml | 78 ++++++++++++++++++++++----- 10 files changed, 155 insertions(+), 19 deletions(-) diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index a587d0aa0..d395b14c6 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -173,6 +173,7 @@ static const char* kObservedPrefs[] = { }; nsFocusManager::nsFocusManager() + : mEventHandlingNeedsFlush(false) { } nsFocusManager::~nsFocusManager() @@ -1522,6 +1523,7 @@ nsFocusManager::CheckIfFocusable(nsIContent* aContent, uint32_t aFlags) } // Make sure that our frames are up to date + mEventHandlingNeedsFlush = false; doc->FlushPendingNotifications(Flush_Layout); nsIPresShell *shell = doc->GetShell(); diff --git a/dom/base/nsFocusManager.h b/dom/base/nsFocusManager.h index a2aad11dc..86bacd390 100644 --- a/dom/base/nsFocusManager.h +++ b/dom/base/nsFocusManager.h @@ -8,6 +8,7 @@ #define nsFocusManager_h___ #include "nsCycleCollectionParticipant.h" +#include "nsIContent.h" #include "nsIDocument.h" #include "nsIFocusManager.h" #include "nsIObserver.h" @@ -93,6 +94,29 @@ public: return handlingDocument.forget(); } + void NeedsFlushBeforeEventHandling(nsIContent* aContent) + { + if (mFocusedContent == aContent) { + mEventHandlingNeedsFlush = true; + } + } + + bool CanSkipFocus(nsIContent* aContent) + { + return mFocusedContent == aContent; + } + + void FlushBeforeEventHandlingIfNeeded(nsIContent* aContent) + { + if (mEventHandlingNeedsFlush) { + nsCOMPtr doc = aContent->GetComposedDoc(); + if (doc) { + mEventHandlingNeedsFlush = false; + doc->FlushPendingNotifications(Flush_Layout); + } + } + } + /** * Update the caret with current mode (whether in caret browsing mode or not). */ @@ -513,6 +537,10 @@ private: // moving focus. nsCOMPtr mMouseButtonEventHandlingDocument; + // If set to true, layout of the document of the event target should be + // flushed before handling focus depending events. + bool mEventHandlingNeedsFlush; + static bool sTestMode; // the single focus manager diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 720b4dcd8..bd4ed1085 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -1465,6 +1465,10 @@ HTMLInputElement::GetValueInternal(nsAString& aValue) const bool HTMLInputElement::IsValueEmpty() const { + if (GetValueMode() == VALUE_MODE_VALUE && IsSingleLineTextControl(false)) { + return !mInputData.mState->HasNonEmptyValue(); + } + nsAutoString value; GetValueInternal(value); @@ -4277,6 +4281,14 @@ HTMLInputElement::UnbindFromTree(bool aDeep, bool aNullParent) void HTMLInputElement::HandleTypeChange(uint8_t aNewType) { + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) { + // Input element can represent very different kinds of UIs, and we may + // need to flush styling even when focusing the already focused input + // element. + fm->NeedsFlushBeforeEventHandling(this); + } + if (mType == NS_FORM_INPUT_RANGE && mIsDraggingRange) { CancelRangeThumbDrag(false); } diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index f3ca091fb..29597df56 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -2668,9 +2668,12 @@ nsGenericHTMLElement::Blur(mozilla::ErrorResult& aError) void nsGenericHTMLElement::Focus(ErrorResult& aError) { - nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + nsFocusManager* fm = nsFocusManager::GetFocusManager(); if (fm) { - aError = fm->SetFocus(this, 0); + if (fm->CanSkipFocus(this)) + fm->NeedsFlushBeforeEventHandling(this); + else + aError = fm->SetFocus(this, 0); } } diff --git a/dom/html/nsTextEditorState.cpp b/dom/html/nsTextEditorState.cpp index 05642674f..4ee0d41e8 100644 --- a/dom/html/nsTextEditorState.cpp +++ b/dom/html/nsTextEditorState.cpp @@ -2174,6 +2174,23 @@ nsTextEditorState::SetValue(const nsAString& aValue, uint32_t aFlags) return true; } +bool +nsTextEditorState::HasNonEmptyValue() +{ + if (mEditor && mBoundFrame && mEditorInitialized && + !mIsCommittingComposition) { + bool empty; + nsresult rv = mEditor->GetDocumentIsEmpty(&empty); + if (NS_SUCCEEDED(rv)) { + return !empty; + } + } + + nsAutoString value; + GetValue(value, true); + return !value.IsEmpty(); +} + void nsTextEditorState::InitializeKeyboardEventListeners() { diff --git a/dom/html/nsTextEditorState.h b/dom/html/nsTextEditorState.h index b4c906e21..678557607 100644 --- a/dom/html/nsTextEditorState.h +++ b/dom/html/nsTextEditorState.h @@ -158,6 +158,11 @@ public: MOZ_WARN_UNUSED_RESULT bool SetValue(const nsAString& aValue, uint32_t aFlags); void GetValue(nsAString& aValue, bool aIgnoreWrap) const; + bool HasNonEmptyValue(); + // The following methods are for textarea element to use whether default + // value or not. + // XXX We might have to add assertion when it is into editable, + // or reconsider fixing bug 597525 to remove these. void EmptyValue() { if (mValue) mValue->Truncate(); } bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; } diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 98cece187..76be28c48 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -7815,6 +7815,13 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, bool touchIsNew = false; bool isHandlingUserInput = false; + if (mCurrentEventContent && aEvent->IsTargetedAtFocusedWindow()) { + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) { + fm->FlushBeforeEventHandlingIfNeeded(mCurrentEventContent); + } + } + // XXX How about IME events and input events for plugins? if (aEvent->mFlags.mIsTrusted) { switch (aEvent->mMessage) { diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 44116634e..44955d3e8 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -396,6 +396,7 @@ nsFrame::nsFrame(nsStyleContext* aContext) MOZ_COUNT_CTOR(nsFrame); mState = NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY; + mMayHaveRoundedCorners = false; mStyleContext = aContext; mStyleContext->AddRef(); #ifdef DEBUG @@ -876,6 +877,7 @@ nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) NS_FRAME_SIMPLE_DISPLAYLIST); */ RemoveStateBits(NS_FRAME_SIMPLE_DISPLAYLIST); + mMayHaveRoundedCorners = true; } // MSVC fails with link error "one or more multiply defined symbols found", @@ -1289,6 +1291,11 @@ nsIFrame::OutsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets) nsIFrame::GetBorderRadii(const nsSize& aFrameSize, const nsSize& aBorderArea, Sides aSkipSides, nscoord aRadii[8]) const { + if (!mMayHaveRoundedCorners) { + memset(aRadii, 0, sizeof(nscoord) * 8); + return false; + } + if (IsThemed()) { // When we're themed, the native theme code draws the border and // background, and therefore it doesn't make sense to tell other @@ -1302,9 +1309,12 @@ nsIFrame::GetBorderRadii(const nsSize& aFrameSize, const nsSize& aBorderArea, } return false; } - return ComputeBorderRadii(StyleBorder()->mBorderRadius, - aFrameSize, aBorderArea, - aSkipSides, aRadii); + + const_cast(this)->mMayHaveRoundedCorners = + ComputeBorderRadii(StyleBorder()->mBorderRadius, + aFrameSize, aBorderArea, + aSkipSides, aRadii); + return mMayHaveRoundedCorners; } bool diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index f5f2ceed2..e514caf27 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -3152,6 +3152,8 @@ protected: VisualDeltas mVisualDeltas; } mOverflow; + bool mMayHaveRoundedCorners : 1; + // Helpers /** * Can we stop inside this frame when we're skipping non-rendered whitespace? diff --git a/toolkit/content/widgets/scrollbox.xml b/toolkit/content/widgets/scrollbox.xml index bb7afb9fb..433620499 100644 --- a/toolkit/content/widgets/scrollbox.xml +++ b/toolkit/content/widgets/scrollbox.xml @@ -196,10 +196,39 @@ null + + + + + @@ -458,29 +487,29 @@ + + + // Try to detect the end of the scrolling animation to update the + // scroll buttons again. To avoid false positives, this timeout needs + // to be big enough to account for intermediate frames that don't move + // the scroll position in case we're scrolling slowly. + this._delayedUpdateScrollButtonsTimer = setTimeout(() => { + // Scrolling animation has finished. + this._delayedUpdateScrollButtonsTimer = 0; + this._updateScrollButtonsDisabledState(); + }, 500); + ]]>