Summary
Clearing the text in a form input is causing a long delay (multiple seconds) in Chrome. It seems that this is causing a reflow or recalculation of puted styles, but I'm not sure. Chrome's Profiler and Timeline were uninformative.
What is causing the delay? How can I eliminate it? Is this an error in my code, or possibly a bug in Chrome?
Example
/
To cause the delay, type into the input and then delete the text. The delay will occur when you delete the final character or if you select all and then delete.
To eliminate the delay, use row4
instead of row1
, row2
, or row3
. Now you can clear the inputs without causing a delay.
Notes
The delay depends on
n
, the number of rows.It occurs in Chrome but not Firefox.
It occurs for me in Linux (Ubuntu/Mint 11) and Windows 7, but not OSX. Can anyone confirm?
I pletely rewrote this question once I had a jsfiddle that exhibits the delay behavior, and rewrote again with a much more simple example.
Summary
Clearing the text in a form input is causing a long delay (multiple seconds) in Chrome. It seems that this is causing a reflow or recalculation of puted styles, but I'm not sure. Chrome's Profiler and Timeline were uninformative.
What is causing the delay? How can I eliminate it? Is this an error in my code, or possibly a bug in Chrome?
Example
http://jsfiddle/jmilloy/dHFsQ/
To cause the delay, type into the input and then delete the text. The delay will occur when you delete the final character or if you select all and then delete.
To eliminate the delay, use row4
instead of row1
, row2
, or row3
. Now you can clear the inputs without causing a delay.
Notes
The delay depends on
n
, the number of rows.It occurs in Chrome but not Firefox.
It occurs for me in Linux (Ubuntu/Mint 11) and Windows 7, but not OSX. Can anyone confirm?
I pletely rewrote this question once I had a jsfiddle that exhibits the delay behavior, and rewrote again with a much more simple example.
Share Improve this question edited Jun 20, 2020 at 9:12 munity wiki14 revs
jmilloy 5
-
The first thing I can think of is that you might want to change
.change(...)
toon("keyup",...)
, just an idea – beardhatcode Commented Apr 1, 2013 at 21:29 -
@gar_onn No, that made no difference. I want the event to occur when you press enter, which is what
.change
does, but I tried changing to keyup and checking for the enter key anyways. The delay still occurs when I hit backspace to clear the last character in the input. In fact, the delay occurs before my keyup event is called! – jmilloy Commented Apr 1, 2013 at 21:57 -
so that is when
peptides.data.length
= 0, in other words, your for loop loops infinitely. Therefore JavaScript will take up a lot op memory and maketrs
insanely large – beardhatcode Commented Apr 1, 2013 at 23:33 - @gar_onn There's a test for that long before. – jmilloy Commented Apr 2, 2013 at 13:35
- This is very odd indeed. Happens on Safari on OSX too. Can't figure it out at the moment .. definitely looks to be some sort of browser bug. Tested it without javascript generating the rows and the same thing occurs. – Mike Campbell Commented Apr 3, 2013 at 8:43
3 Answers
Reset to default 7 +250This appears to be a performance bug in WebKit or a parsing bug in ICU, perhaps both, but I tend to think the issue is in WebKit.
I built Chromium from source on OS X and verified that the build exhibited the problem. It's not clear why it doesn't show up in Chrome on OS X. I profiled the renderer process on your test page and found that it is spending most of its time in ICU in the functions RuleBasedBreakIterator::handleNext()
and RuleBasedBreakIterator::handlePrevious()
. Here's a typical stack trace:
Program received signal SIGINT, Interrupt.
icu_46::RuleBasedBreakIterator::handleNext (this=0x6a1f4600, statetable=0xafdbb50) at ../../third_party/icu/source/mon/rbbi.cpp:1085
1085 if (state == STOP_STATE) {
(gdb) where
#0 icu_46::RuleBasedBreakIterator::handleNext (this=0x6a1f4600, statetable=0xafdbb50) at ../../third_party/icu/source/mon/rbbi.cpp:1085
#1 0x09c2048f in icu_46::RuleBasedBreakIterator::next (this=0x6a1f4600) at ../../third_party/icu/source/mon/rbbi.cpp:535
#2 0x09c23a30 in icu_46::RuleBasedBreakIterator::following (this=0x6a1f4600, offset=2377) at ../../third_party/icu/source/mon/rbbi.cpp:693
#3 0x09c50399 in ubrk_following_46 (bi=0x6a1f4600, offset=2377) at ../../third_party/icu/source/mon/ubrk.cpp:254
#4 0x03112489 in WebCore::textBreakFollowing (iterator=0x6a1f4600, pos=2377) at ../../third_party/WebKit/Source/WebCore/platform/text/TextBreakIteratorICU.cpp:380
#5 0x03111276 in WebCore::findNextWordFromIndex (chars=0x69a7a000, len=2848, position=2377, forward=true) at ../../third_party/WebKit/Source/WebCore/platform/text/TextBoundaries.cpp:77
#6 0x016352d3 in WebCore::nextWordPositionBoundary (characters=0x69a7a000, length=2848, offset=0, mayHaveMoreContext=WebCore::MayHaveMoreContext, needMoreContext=@0xc00078b2) at ../../third_party/WebKit/Source/WebCore/editing/VisibleUnits.cpp:695
#7 0x0163498a in WebCore::nextBoundary (c=@0xc0008440, searchFunction=0x1635220 <WebCore::nextWordPositionBoundary(unsigned short const*, unsigned int, unsigned int, WebCore::BoundarySearchContextAvailability, bool&)>) at ../../third_party/WebKit/Source/WebCore/editing/VisibleUnits.cpp:575
#8 0x016351c5 in WebCore::nextWordPosition (c=@0xc0008440) at ../../third_party/WebKit/Source/WebCore/editing/VisibleUnits.cpp:700
#9 0x0159d2ce in WebCore::Editor::updateMarkersForWordsAffectedByEditing (this=0x6abca710, doNotRemoveIfSelectionAtWordBoundary=true) at ../../third_party/WebKit/Source/WebCore/editing/Editor.cpp:2398
#10 0x0159ce7c in WebCore::Editor::respondToChangedContents (this=0x6abca710, endingSelection=@0xc0008578) at ../../third_party/WebKit/Source/WebCore/editing/Editor.cpp:556
#11 0x0159fa81 in WebCore::Editor::appliedEditing (this=0x6abca710, cmd=@0xc00085f0) at ../../third_party/WebKit/Source/WebCore/editing/Editor.cpp:863
#12 0x0162597a in WebCore::TypingCommand::typingAddedToOpenCommand (this=0x6a570fb0, mandTypeForAddedTyping=WebCore::TypingCommand::DeleteKey) at ../../third_party/WebKit/Source/WebCore/editing/TypingCommand.cpp:347
#13 0x01622e05 in WebCore::TypingCommand::deleteKeyPressed (this=0x6a570fb0, granularity=WebCore::CharacterGranularity, killRing=false) at ../../third_party/WebKit/Source/WebCore/editing/TypingCommand.cpp:525
#14 0x01621bfa in WebCore::TypingCommand::deleteKeyPressed (document=0x6abcc200, options=0, granularity=WebCore::CharacterGranularity) at ../../third_party/WebKit/Source/WebCore/editing/TypingCommand.cpp:120
#15 0x0159b945 in WebCore::Editor::deleteWithDirection (this=0x6abca710, direction=WebCore::DirectionBackward, granularity=WebCore::CharacterGranularity, killRing=false, isTypingAction=true) at ../../third_party/WebKit/Source/WebCore/editing/Editor.cpp:382
#16 0x015b5cc8 in WebCore::executeDeleteBackward (frame=0x6abca200) at ../../third_party/WebKit/Source/WebCore/editing/EditorCommand.cpp:339
#17 0x015b4e7f in WebCore::Editor::Command::execute (this=0xc0008c28, parameter=@0xc0008c20, triggeringEvent=0x0) at ../../third_party/WebKit/Source/WebCore/editing/EditorCommand.cpp:1706
#18 0x0419f8c4 in WebKit::WebFrameImpl::executeCommand (this=0x6a31f770, name=@0xc0008ca0, value=@0xc0008c98) at ../../third_party/WebKit/Source/WebKit/chromium/src/WebFrameImpl.cpp:1245
#19 0x07d5e3a1 in content::RenderViewImpl::handleCurrentKeyboardEvent (this=0x6a998000) at ../../content/renderer/render_view_impl:2189
#20 0x07d5e452 in non-virtual thunk to content::RenderViewImpl::handleCurrentKeyboardEvent() (this=0x6a998314) at ../../content/renderer/render_view_impl:2195
#21 0x040f97dc in WebKit::EditorClientImpl::handleKeyboardEvent (this=0x6a9ba674, evt=0x190468e0) at ../../third_party/WebKit/Source/WebKit/chromium/src/EditorClientImpl.cpp:645
#22 0x01599799 in WebCore::Editor::handleKeyboardEvent (this=0x6abca710, event=0x190468e0) at ../../third_party/WebKit/Source/WebCore/editing/Editor.cpp:212
#23 0x01a9713d in WebCore::EventHandler::defaultKeyboardEventHandler (this=0x6abca854, event=0x190468e0) at ../../third_party/WebKit/Source/WebCore/page/EventHandler.cpp:3325
#24 0x07790701 in WebCore::Node::defaultEventHandler (this=0x19049eb0, event=0x190468e0) at ../../third_party/WebKit/Source/WebCore/dom/Node.cpp:2475
#25 0x048aa253 in WebCore::HTMLTextFormControlElement::defaultEventHandler (this=0x19049eb0, event=0x190468e0) at ../../third_party/WebKit/Source/WebCore/html/HTMLTextFormControlElement.cpp:111
#26 0x0480cf5f in WebCore::HTMLInputElement::defaultEventHandler (this=0x19049eb0, evt=0x190468e0) at ../../third_party/WebKit/Source/WebCore/html/HTMLInputElement.cpp:1159
#27 0x0772ac59 in WebCore::EventDispatcher::dispatchEventPostProcess (this=0xc0009048, preDispatchEventHandlerResult=0x0) at ../../third_party/WebKit/Source/WebCore/dom/EventDispatcher.cpp:208
#28 0x07729cc6 in WebCore::EventDispatcher::dispatch (this=0xc0009048) at ../../third_party/WebKit/Source/WebCore/dom/EventDispatcher.cpp:127
#29 0x07728b03 in WebCore::EventDispatchMediator::dispatchEvent (this=0x190f6f90, dispatcher=0xc0009048) at ../../third_party/WebKit/Source/WebCore/dom/EventDispatchMediator.cpp:54
#30 0x07728f50 in WebCore::EventDispatcher::dispatchEvent (node=0x19049eb0, mediator=@0xc0009130) at ../../third_party/WebKit/Source/WebCore/dom/EventDispatcher.cpp:56
#31 0x0778f078 in WebCore::Node::dispatchEvent (this=0x19049eb0, event=@0xc0009198) at ../../third_party/WebKit/Source/WebCore/dom/Node.cpp:2351
#32 0x077405e1 in WebCore::EventTarget::dispatchEvent (this=0x19049eb0, event=@0xc00092e8, ec=@0xc00092e0) at ../../third_party/WebKit/Source/WebCore/dom/EventTarget.cpp:147
#33 0x01a9688a in WebCore::EventHandler::keyEvent (this=0x6abca854, initialKeyEvent=@0xc00093c0) at ../../third_party/WebKit/Source/WebCore/page/EventHandler.cpp:3207
#34 0x04236521 in WebKit::WebViewImpl::handleKeyEvent (this=0x6a9ba600, event=@0x1900f2e8) at ../../third_party/WebKit/Source/WebKit/chromium/src/WebViewImpl.cpp:1003
#35 0x04236def in non-virtual thunk to WebKit::WebViewImpl::handleKeyEvent(WebKit::WebKeyboardEvent const&) (this=0x6a9ba628, event=@0x1900f2e8) at ../../third_party/WebKit/Source/WebKit/chromium/src/WebViewImpl.cpp:1015
#36 0x0413554d in WebKit::PageWidgetDelegate::handleInputEvent (page=0x6a9baa00, handler=@0x6a9ba628, event=@0x1900f2e8) at ../../third_party/WebKit/Source/WebKit/chromium/src/PageWidgetDelegate.cpp:144
#37 0x0423bebb in WebKit::WebViewImpl::handleInputEvent (this=0x6a9ba600, inputEvent=@0x1900f2e8) at ../../third_party/WebKit/Source/WebKit/chromium/src/WebViewImpl.cpp:2058
#38 0x07db4d90 in content::RenderWidget::OnHandleInputEvent (this=0x6a998000, input_event=0x1900f2e8, is_keyboard_shortcut=true) at ../../content/renderer/render_widget:732
#39 0x07dca53b in DispatchToMethod<content::RenderWidget, void (content::RenderWidget::*)(WebKit::WebInputEvent const*, bool), WebKit::WebInputEvent const*, bool> (obj=0x6a998000, method=not implemented: member type in c_val_print
) at tuple.h:553
#40 0x07dc2235 in ViewMsg_HandleInputEvent::Dispatch<content::RenderWidget, content::RenderWidget, void (content::RenderWidget::*)(WebKit::WebInputEvent const*, bool)> (msg=0x190279c8, obj=0x6a998000, sender=0x6a998000, func=not implemented: member type in c_val_print
) at view_messages.h:867
#41 0x07db3488 in content::RenderWidget::OnMessageReceived (this=0x6a998000, message=@0x190279c8) at ../../content/renderer/render_widget:307
#42 0x07d50bae in content::RenderViewImpl::OnMessageReceived (this=0x6a998000, message=@0x190279c8) at ../../content/renderer/render_view_impl:1136
#43 0x082c9133 in content::MessageRouter::RouteMessage (this=0x190a252c, msg=@0x190279c8) at ../../content/mon/message_router:49
#44 0x082c9085 in content::MessageRouter::OnMessageReceived (this=0x190a252c, msg=@0x190279c8) at ../../content/mon/message_router:41
#45 0x07f410b9 in content::ChildThread::OnMessageReceived (this=0x190a2514, msg=@0x190279c8) at ../../content/mon/child_thread:274
#46 0x03fec745 in IPC::ChannelProxy::Context::OnDispatchMessage (this=0x19098a40, message=@0x190279c8) at ../../ipc/ipc_channel_proxy:261
#47 0x03ff5920 in base::internal::RunnableAdapter<void (IPC::ChannelProxy::Context::*)(IPC::Message const&)>::Run (this=0xc000b1d0, object=0x19098a40, a1=@0x190279c8) at bind_internal.h:190
#48 0x03ff580f in base::internal::InvokeHelper<false, void, base::internal::RunnableAdapter<void (IPC::ChannelProxy::Context::*)(IPC::Message const&)>, void ()(IPC::ChannelProxy::Context* const&, IPC::Message const&)>::MakeItSo (runnable=Unexpected type (16) encountered for integer constant.
) at bind_internal.h:899
#49 0x03ff5744 in base::internal::Invoker<2, base::internal::BindState<base::internal::RunnableAdapter<void (IPC::ChannelProxy::Context::*)(IPC::Message const&)>, void ()(IPC::ChannelProxy::Context*, IPC::Message const&), void ()(IPC::ChannelProxy::Context*, IPC::Message)>, void ()(IPC::ChannelProxy::Context*, IPC::Message const&)>::Run (base=0x190279b0) at bind_internal.h:1257
#50 0x026c51fb in base::Callback<void ()()>::Run (this=0xc000b464) at callback.h:396
#51 0x0438583a in base::MessageLoop::RunTask (this=0xc000d058, pending_task=@0xc000b450) at ../../base/message_loop:474
#52 0x04385c92 in base::MessageLoop::DeferOrRunPendingTask (this=0xc000d058, pending_task=@0xc000b450) at ../../base/message_loop:486
#53 0x04385e92 in base::MessageLoop::DoWork (this=0xc000d058) at ../../base/message_loop:669
#54 0x042e280b in base::MessagePumpCFRunLoopBase::RunWork (this=0x6a16ef60) at ../../base/message_pump_mac.mm:252
#55 0x042e1fc2 in base::MessagePumpCFRunLoopBase::RunWorkSource (info=0x6a16ef60) at ../../base/message_pump_mac.mm:230
#56 0x9bc7e13f in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#57 0x9bc7daf6 in __CFRunLoopDoSources0 ()
#58 0x9bca79c8 in __CFRunLoopRun ()
#59 0x9bca71dc in CFRunLoopRunSpecific ()
#60 0x9bca7088 in CFRunLoopRunInMode ()
#61 0x92f83543 in RunCurrentEventLoopInMode ()
#62 0x92f8a8ab in ReceiveNextEventCommon ()
#63 0x92f8a71a in BlockUntilNextEventMatchingListInMode ()
#64 0x99b31ee8 in _DPSNextEvent ()
#65 0x99b31752 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
#66 0x99b2dac1 in -[NSApplication run] ()
#67 0x042e372e in base::MessagePumpNSApplication::DoRun (this=0x6a16ef60, delegate=0xc000d058) at ../../base/message_pump_mac.mm:576
#68 0x042e2578 in base::MessagePumpCFRunLoopBase::Run (this=0x6a16ef60, delegate=0xc000d058) at ../../base/message_pump_mac.mm:171
#69 0x043850a2 in base::MessageLoop::RunInternal (this=0xc000d058) at ../../base/message_loop:431
#70 0x04384f5b in base::MessageLoop::RunHandler (this=0xc000d058) at ../../base/message_loop:404
#71 0x043ea808 in base::RunLoop::Run (this=0xc000ccc0) at ../../base/run_loop:45
#72 0x04384357 in base::MessageLoop::Run (this=0xc000d058) at ../../base/message_loop:311
#73 0x07dd3a14 in content::RendererMain (parameters=@0xc000d348) at ../../content/renderer/renderer_main:226
#74 0x086f4324 in content::RunNamedProcessTypeMain (process_type=@0xc000d368, main_function_params=@0xc000d348, delegate=0xc000d5a0) at ../../content/app/content_main_runner:459
#75 0x086f5788 in content::ContentMainRunnerImpl::Run (this=0x6a16c080) at ../../content/app/content_main_runner:764
#76 0x086f3457 in content::ContentMain (argc=7, argv=0xc000d620, delegate=0xc000d5a0) at ../../content/app/content_main:35
#77 0x00017d2c in ChromeMain (argc=7, argv=0xc000d620) at ../../chrome/app/chrome_main:32
#78 0x0000ff7b in main (argc=7, argv=0xc000d620) at ../../chrome/app/chrome_exe_main_mac:16
(gdb)
The bug does not appear to be in layout (although it does look like layout is being updated). It looks like the problem is in updating spell-checking markers - that's what WebCore::Editor::updateMarkersForWordsAffectedByEditing()
does. The time is being spent trying to figure out the range of the content to update - it's using the RuleBasedBreakIterator
to make sure that range begins and ends on a "word" boundary. The search for the boundary basically keeps adding a character and asks, "Is this a plete word?" That's O(n^2) on the length of the word but for some reason the test for a plete word keeps failing and n is in the thousands... I verified with timings that the behavior is indeed O(n^2) in the number of table rows. Here's the search loop in WebCore::nextBoundary()
(searchFunction
is WebCore::nextWordPositionBoundary
and is what tests for a plete word):
while (!it.atEnd()) {
// Keep asking the iterator for chunks until the search function
// returns an end value not equal to the length of the string passed to it.
if (!inTextSecurityMode)
string.append(it.characters(), it.length());
else {
// Treat bullets used in the text security mode as regular characters when looking for boundaries
String iteratorString(it.characters(), it.length());
iteratorString.fill('x');
string.append(iteratorString.characters(), iteratorString.length());
}
next = searchFunction(string.data(), string.size(), prefixLength, MayHaveMoreContext, needMoreContext);
if (next != string.size())
break;
it.advance();
}
The contents of the string appear to be ",\n…\n…\n…" in UTF-16 for however many ellipsis rows you have. I don't know why WebCore::nextWordPositionBoundary()
isn't detecting a boundary at the newline, and I'm also not sure why it is looking outside the <input>
tag text.
I verified that the bug exists in the WebKit nightly build and filed a bug with WebKit.
This is a bug in WebKit (homepage / Wikipedia Article).
I've been able to reproduce this issue in the following OS/browser binations:
Windows 7 SP1 / Chrome 26.0.1410.43 m
OS X 10.8.3 / Safari 6.0.3 (8536.28.10)
Arch Linux 3.8.5-1 / Chromium 26.0.1410.43 (189671) (prepiled)
Ubuntu 12.10 / Chromium 25.0.1364.160-0ubuntu0.12.10.1 (prepiled, available from the Ubuntu Software Center)
Hence, it's not fixable through JavaScript per se - your solution is to use a different display method. Such as:
<tr><td style="border-bottom: dotted 1px #000; width: 5px; height: 10px;"></td></tr>
or
<tr><td><span style="display: inline-block; border-bottom: dotted 1px #000; width: 10px;"></span></td></tr>
Here's a jsFiddle - depending on your intent, one or the other may work. Unfortunately, I tried using …
and ...
, to no avail.
As mentioned, this behavior is not seen on Firefox, nor:
Windows 7 SP1 / Internet Explorer 10
iPhone iOS 6.1.2 / Safari
iPad iOS 6.1.3 / Safari
Just an FYI for those unaware
WebKit: WebKit is a layout engine software designed to allow web browsers to render web pages.(Wikipedia) WebKit is currently used by Chrome, Chromium, and Safari (as of this writing, 2013-04-05).
Chromium: Chromium is the open source web browser project from which Google Chrome draws its source code. The browsers share the majority of code and features, though there are some minor differences.(Wikipedia)
In this section of code:
for (var i=0; i<peptides.data.length; i++)
trs += peptides.row(peptides.data[i]);
There's the potential for an infinite loop if you're adding or changing the length of peptides.data
in any way. This is because peptides.data.length
is evaluated for every iteration of the loop. It doesn't look like this is happening in the above code but you did say that it is an abridged version. Either way, your code will be a good bit faster if you didn't have to evaluate peptides.data.length
every time so it's worth changing anyway:
for (var i=0, len = peptides.data.length; i<len; i++)
...
Edit- Now that this question has been updated, and I can clearly see the problem OP is describing; I too would be interested to see if some people smarter than I can work out what's going on here.