mirror of
https://github.com/classilla/tenfourfox.git
synced 2025-02-05 10:31:24 +00:00
parent
467573125f
commit
d22246efad
@ -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();
|
||||
|
@ -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<nsIDocument> 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<nsIDocument> 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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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<nsIFrame*>(this)->mMayHaveRoundedCorners =
|
||||
ComputeBorderRadii(StyleBorder()->mBorderRadius,
|
||||
aFrameSize, aBorderArea,
|
||||
aSkipSides, aRadii);
|
||||
return mMayHaveRoundedCorners;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -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?
|
||||
|
@ -196,10 +196,39 @@
|
||||
|
||||
<field name="_scrollTarget">null</field>
|
||||
|
||||
<method name="_boundsWithoutFlushing">
|
||||
<parameter name="element"/>
|
||||
<body><![CDATA[
|
||||
if (!("_DOMWindowUtils" in this)) {
|
||||
try {
|
||||
this._DOMWindowUtils =
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
} catch (e) {
|
||||
// Can't access nsIDOMWindowUtils if we're unprivileged.
|
||||
this._DOMWindowUtils = null;
|
||||
}
|
||||
}
|
||||
|
||||
return this._DOMWindowUtils ?
|
||||
this._DOMWindowUtils.getBoundsWithoutFlushing(element) :
|
||||
element.getBoundingClientRect();
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_canScrollToElement">
|
||||
<parameter name="element"/>
|
||||
<body><![CDATA[
|
||||
return window.getComputedStyle(element).display != "none";
|
||||
if (element.hidden) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// See if the element is hidden via CSS without the hidden attribute.
|
||||
// If we get only zeros for the client rect, this means the element
|
||||
// is hidden. As a performance optimization, we don't flush layout
|
||||
// here which means that on the fly changes aren't fully supported.
|
||||
let rect = this._boundsWithoutFlushing(element);
|
||||
return !!(rect.top || rect.left || rect.width || rect.height);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
@ -458,29 +487,29 @@
|
||||
</method>
|
||||
|
||||
<method name="_updateScrollButtonsDisabledState">
|
||||
<parameter name="aScrolling"/>
|
||||
<body><![CDATA[
|
||||
var scrolledToStart = false;
|
||||
var scrolledToEnd = false;
|
||||
let scrolledToStart;
|
||||
let scrolledToEnd;
|
||||
|
||||
// Avoid flushing layout when not overflowing or when scrolling.
|
||||
if (this.hasAttribute("notoverflowing")) {
|
||||
scrolledToStart = true;
|
||||
scrolledToEnd = true;
|
||||
}
|
||||
else if (this.scrollPosition == 0) {
|
||||
} else if (aScrolling) {
|
||||
scrolledToStart = false;
|
||||
scrolledToEnd = false;
|
||||
} else if (this.scrollPosition == 0) {
|
||||
// In the RTL case, this means the _last_ element in the
|
||||
// scrollbox is visible
|
||||
if (this._isRTLScrollbox)
|
||||
scrolledToEnd = true;
|
||||
else
|
||||
scrolledToStart = true;
|
||||
scrolledToEnd = this._isRTLScrollbox;
|
||||
scrolledToStart = !this._isRTLScrollbox;
|
||||
}
|
||||
else if (this.scrollClientSize + this.scrollPosition == this.scrollSize) {
|
||||
// In the RTL case, this means the _first_ element in the
|
||||
// scrollbox is visible
|
||||
if (this._isRTLScrollbox)
|
||||
scrolledToStart = true;
|
||||
else
|
||||
scrolledToEnd = true;
|
||||
scrolledToStart = this._isRTLScrollbox;
|
||||
scrolledToEnd = !this._isRTLScrollbox;
|
||||
}
|
||||
|
||||
if (scrolledToEnd)
|
||||
@ -600,8 +629,29 @@
|
||||
this.setAttribute("notoverflowing", "true");
|
||||
}
|
||||
]]></handler>
|
||||
<handler event="scroll"><![CDATA[
|
||||
if (!this._delayedUpdateScrollButtonsTimer) {
|
||||
// This is the beginning of a scrolling animation. We need to update
|
||||
// scroll buttons now in case we were scrolled to the start or to the
|
||||
// end before we started scrolling.
|
||||
this._updateScrollButtonsDisabledState(true);
|
||||
} else {
|
||||
// We're in the middle of the scrolling animation. We'll restart the
|
||||
// delayed update request so that we only update the scroll buttons
|
||||
// a second time once we're done scrolling.
|
||||
window.clearTimeout(this._delayedUpdateScrollButtonsTimer);
|
||||
}
|
||||
|
||||
<handler event="scroll" action="this._updateScrollButtonsDisabledState()"/>
|
||||
// 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);
|
||||
]]></handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user