Skip to content

Commit 7d2e6e3

Browse files
committed
Avoid injected bundle delegate calls when text fields are focused and blurred without user interaction
https://bugs.webkit.org/show_bug.cgi?id=240614 Reviewed by Chris Dumez. Add a mechanism to throttle calls to injected bundle form client via `textFieldDidBeginEditing`, in the case where the focused element is in a subframe that has never handled an editing command or user interaction. This yields a small win on Speedometer 2, on M1 MacBookPro: ``` ---------------------------------------------------------------------------------------------------------- | subtest | ms | ms | b / a | pValue | ---------------------------------------------------------------------------------------------------------- | Angular2-TypeScript-TodoMVC |30.651667 |28.456667 |0.928389 | 0.000000 (significant) | | AngularJS-TodoMVC |109.733333 |111.623333 |1.017224 | 0.000000 (significant) | | BackboneJS-TodoMVC |31.371667 |33.988333 |1.083409 | 0.000000 (significant) | | Elm-TodoMVC |96.818333 |96.893333 |1.000775 | 0.760078 | | EmberJS-Debug-TodoMVC |292.241667 |292.628333 |1.001323 | 0.530967 | | EmberJS-TodoMVC |99.671667 |98.963333 |0.992893 | 0.040683 | | Flight-TodoMVC |43.815000 |50.915000 |1.162045 | 0.000000 (significant) | | Inferno-TodoMVC |46.633333 |44.911667 |0.963081 | 0.000000 (significant) | | Preact-TodoMVC |11.636667 |11.673333 |1.003151 | 0.862258 | | React-Redux-TodoMVC |121.411667 |120.096667 |0.989169 | 0.000000 (significant) | | React-TodoMVC |69.908333 |69.885000 |0.999666 | 0.944581 | | Vanilla-ES2015-Babel-Webpack-TodoMVC |47.750000 |46.223333 |0.968028 | 0.000000 (significant) | | Vanilla-ES2015-TodoMVC |48.721667 |48.323333 |0.991824 | 0.001202 (significant) | | VanillaJS-TodoMVC |40.218333 |38.231667 |0.950603 | 0.000000 (significant) | | VueJS-TodoMVC |18.420000 |16.793333 |0.911690 | 0.000000 (significant) | | jQuery-TodoMVC |188.831667 |186.908333 |0.989815 | 0.000005 (significant) | ---------------------------------------------------------------------------------------------------------- a mean = 343.48012 b mean = 344.99902 pValue = 0.0027314347 (Bigger means are better.) 1.004 times better Results ARE significant ``` See below for more details. * Source/WebCore/editing/Editor.cpp: (WebCore::Editor::Editor): (WebCore::Editor::stopTextFieldDidBeginEditingTimer): Stop the `textFieldDidBeginEditing` timer if needed, and return true if and only if it was active. (WebCore::Editor::textFieldDidBeginEditingTimerFired): Dispatch the deferred EditorClient call using the currently focused element. (WebCore::Editor::textFieldDidBeginEditing): If we're inside of a subframe that has never handled user interaction or editing, then don't eagerly notify the injected bundle about the newly focused text field; instead, schedule a newly added timer (`m_textFieldDidBeginEditingTimer`) to perform this call after a short delay. (WebCore::Editor::textFieldDidEndEditing): If editing ends (i.e. the text field is blurred) while the `textFieldDidBeginEditing` timer is still scheduled, then simply elide this call to `textFieldDidBeginEditing` and `textFieldDidEndEditing` altogether. This prevents us from repeatedly calling into the injected bundle if a page frequently programmatically focuses and blurs text fields. (WebCore::Editor::textDidChangeInTextField): (WebCore::Editor::doTextFieldCommandFromEvent): (WebCore::Editor::textWillBeDeletedInTextField): (WebCore::Editor::textDidChangeInTextArea): If any of these other injected bundle form client hooks are invoked while there is a scheduled `textFieldDidBeginEditing` timer, then stop the timer and immediately inform the injected bundle client about the focused text field. (WebCore::Editor::isInSubframeWithoutUserInteraction const): (WebCore::Editor::respondToChangedSelection): Use the new helper function above. * Source/WebCore/editing/Editor.h: Canonical link: https://commits.webkit.org/250771@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@294514 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent f0527ad commit 7d2e6e3

File tree

2 files changed

+86
-19
lines changed

2 files changed

+86
-19
lines changed

Source/WebCore/editing/Editor.cpp

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@
138138

139139
namespace WebCore {
140140

141+
constexpr auto textFieldDidBeginEditingClientNotificationDelay = 500_ms;
142+
141143
static bool dispatchBeforeInputEvent(Element& element, const AtomString& inputType, const String& data = { }, RefPtr<DataTransfer>&& dataTransfer = nullptr, const Vector<RefPtr<StaticRange>>& targetRanges = { }, Event::IsCancelable cancelable = Event::IsCancelable::Yes)
142144
{
143145
auto event = InputEvent::create(eventNames().beforeinputEvent, inputType, cancelable, element.document().windowProxy(), data, WTFMove(dataTransfer), targetRanges, 0);
@@ -1246,6 +1248,7 @@ Editor::Editor(Document& document)
12461248
#if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS_FAMILY)
12471249
, m_telephoneNumberDetectionUpdateTimer(*this, &Editor::scanSelectionForTelephoneNumbers, 0_s)
12481250
#endif
1251+
, m_textFieldDidBeginEditingTimer(*this, &Editor::textFieldDidBeginEditingTimerFired)
12491252
{
12501253
}
12511254

@@ -3462,43 +3465,95 @@ void Editor::computeAndSetTypingStyle(StyleProperties& properties, EditAction ed
34623465
return computeAndSetTypingStyle(EditingStyle::create(&properties), editingAction);
34633466
}
34643467

3465-
void Editor::textFieldDidBeginEditing(Element& e)
3468+
bool Editor::stopTextFieldDidBeginEditingTimer()
34663469
{
3467-
if (client())
3468-
client()->textFieldDidBeginEditing(e);
3470+
if (m_textFieldDidBeginEditingTimer.isActive()) {
3471+
m_textFieldDidBeginEditingTimer.stop();
3472+
return true;
3473+
}
3474+
return false;
3475+
}
3476+
3477+
void Editor::textFieldDidBeginEditingTimerFired()
3478+
{
3479+
auto* client = this->client();
3480+
if (!client)
3481+
return;
3482+
3483+
if (RefPtr element = m_document.activeElement())
3484+
client->textFieldDidBeginEditing(*element);
34693485
}
34703486

3471-
void Editor::textFieldDidEndEditing(Element& e)
3487+
void Editor::textFieldDidBeginEditing(Element& element)
3488+
{
3489+
auto* client = this->client();
3490+
if (!client)
3491+
return;
3492+
3493+
if (isInSubframeWithoutUserInteraction()) {
3494+
m_textFieldDidBeginEditingTimer.startOneShot(textFieldDidBeginEditingClientNotificationDelay);
3495+
return;
3496+
}
3497+
3498+
client->textFieldDidBeginEditing(element);
3499+
}
3500+
3501+
void Editor::textFieldDidEndEditing(Element& element)
34723502
{
34733503
dismissCorrectionPanelAsIgnored();
3474-
if (client())
3475-
client()->textFieldDidEndEditing(e);
3504+
3505+
auto* client = this->client();
3506+
if (!client)
3507+
return;
3508+
3509+
if (stopTextFieldDidBeginEditingTimer())
3510+
return;
3511+
3512+
client->textFieldDidEndEditing(element);
34763513
}
34773514

3478-
void Editor::textDidChangeInTextField(Element& e)
3515+
void Editor::textDidChangeInTextField(Element& element)
34793516
{
3480-
if (client())
3481-
client()->textDidChangeInTextField(e);
3517+
auto* client = this->client();
3518+
if (!client)
3519+
return;
3520+
3521+
if (stopTextFieldDidBeginEditingTimer())
3522+
client->textFieldDidBeginEditing(element);
3523+
client->textDidChangeInTextField(element);
34823524
}
34833525

3484-
bool Editor::doTextFieldCommandFromEvent(Element& e, KeyboardEvent* ke)
3526+
bool Editor::doTextFieldCommandFromEvent(Element& element, KeyboardEvent* event)
34853527
{
3486-
if (client())
3487-
return client()->doTextFieldCommandFromEvent(e, ke);
3528+
auto* client = this->client();
3529+
if (!client)
3530+
return false;
34883531

3489-
return false;
3532+
if (stopTextFieldDidBeginEditingTimer())
3533+
client->textFieldDidBeginEditing(element);
3534+
return client->doTextFieldCommandFromEvent(element, event);
34903535
}
34913536

34923537
void Editor::textWillBeDeletedInTextField(Element& input)
34933538
{
3494-
if (client())
3495-
client()->textWillBeDeletedInTextField(input);
3539+
auto* client = this->client();
3540+
if (!client)
3541+
return;
3542+
3543+
if (stopTextFieldDidBeginEditingTimer())
3544+
client->textFieldDidBeginEditing(input);
3545+
client->textWillBeDeletedInTextField(input);
34963546
}
34973547

3498-
void Editor::textDidChangeInTextArea(Element& e)
3548+
void Editor::textDidChangeInTextArea(Element& element)
34993549
{
3500-
if (client())
3501-
client()->textDidChangeInTextArea(e);
3550+
auto* client = this->client();
3551+
if (!client)
3552+
return;
3553+
3554+
if (stopTextFieldDidBeginEditingTimer())
3555+
client->textFieldDidBeginEditing(element);
3556+
client->textDidChangeInTextArea(element);
35023557
}
35033558

35043559
void Editor::applyEditingStyleToBodyElement() const
@@ -3686,6 +3741,11 @@ void Editor::selectionWillChange()
36863741
}
36873742
#endif
36883743

3744+
bool Editor::isInSubframeWithoutUserInteraction() const
3745+
{
3746+
return !m_hasHandledAnyEditing && !m_document.hasHadUserInteraction() && !m_document.isTopDocument();
3747+
}
3748+
36893749
void Editor::respondToChangedSelection(const VisibleSelection&, OptionSet<FrameSelection::SetSelectionOption> options)
36903750
{
36913751
#if PLATFORM(IOS_FAMILY)
@@ -3705,7 +3765,7 @@ void Editor::respondToChangedSelection(const VisibleSelection&, OptionSet<FrameS
37053765
setStartNewKillRingSequence(true);
37063766
m_imageElementsToLoadBeforeRevealingSelection.clear();
37073767

3708-
if (!m_hasHandledAnyEditing && !m_document.hasHadUserInteraction() && !m_document.isTopDocument())
3768+
if (isInSubframeWithoutUserInteraction())
37093769
return;
37103770

37113771
if (m_editorUIUpdateTimer.isActive())

Source/WebCore/editing/Editor.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,8 @@ class Editor {
632632

633633
std::optional<SimpleRange> adjustedSelectionRange();
634634

635+
bool isInSubframeWithoutUserInteraction() const;
636+
635637
#if PLATFORM(COCOA)
636638
RefPtr<SharedBuffer> selectionInWebArchiveFormat();
637639
String selectionInHTMLFormat();
@@ -647,6 +649,9 @@ class Editor {
647649
void notifyClientOfAttachmentUpdates();
648650
#endif
649651

652+
bool stopTextFieldDidBeginEditingTimer();
653+
void textFieldDidBeginEditingTimerFired();
654+
650655
String platformContentTypeForBlobType(const String& type) const;
651656

652657
void postTextStateChangeNotificationForCut(const String&, const VisibleSelection&);
@@ -690,6 +695,8 @@ class Editor {
690695
Vector<SimpleRange> m_detectedTelephoneNumberRanges;
691696
#endif
692697

698+
Timer m_textFieldDidBeginEditingTimer;
699+
693700
mutable std::unique_ptr<ScrollView::ProhibitScrollingWhenChangingContentSizeForScope> m_prohibitScrollingDueToContentSizeChangesWhileTyping;
694701

695702
bool m_isGettingDictionaryPopupInfo { false };

0 commit comments

Comments
 (0)