RESOLVED FIXED 122313
FocusController::advanceFocus spends a lot of time in HTMLMapElement::imageElement
https://bugs.webkit.org/show_bug.cgi?id=122313
Summary FocusController::advanceFocus spends a lot of time in HTMLMapElement::imageEl...
Ryosuke Niwa
Reported 2013-10-03 21:03:01 PDT
On a web page with lots of map/area elements, we end up spending a lot of time inside HTMLMapElement::imageElement because the HTMLCollection used to traverse through image elements isn't cached, resulting in O(n^2) time complexity. Running Time Self Symbol Name 7711.0ms 100.0% 0.0 WebKit::handleKeyEvent(WebKit::WebKeyboardEvent const&, WebCore::Page*) 7711.0ms 100.0% 0.0 WebCore::EventHandler::keyEvent(WebCore::PlatformKeyboardEvent const&) 7711.0ms 100.0% 0.0 WebCore::EventTarget::dispatchEvent(WTF::PassRefPtr<WebCore::Event>, int&) 7711.0ms 100.0% 0.0 WebCore::Node::dispatchEvent(WTF::PassRefPtr<WebCore::Event>) 7711.0ms 100.0% 0.0 WebCore::EventDispatcher::dispatchEvent(WebCore::Node*, WTF::PassRefPtr<WebCore::EventDispatchMediator>) 7711.0ms 100.0% 0.0 WebCore::EventDispatchMediator::dispatchEvent(WebCore::EventDispatcher*) const 7711.0ms 100.0% 0.0 WebCore::EventDispatcher::dispatch() 7711.0ms 100.0% 0.0 WebCore::EventDispatcher::dispatchEventPostProcess(void*) 7711.0ms 100.0% 0.0 WebCore::HTMLInputElement::defaultEventHandler(WebCore::Event*) 7711.0ms 100.0% 0.0 WebCore::Node::defaultEventHandler(WebCore::Event*) 7711.0ms 100.0% 0.0 WebCore::EventHandler::defaultKeyboardEventHandler(WebCore::KeyboardEvent*) 7711.0ms 100.0% 0.0 WebCore::FocusController::advanceFocus(WebCore::FocusDirection, WebCore::KeyboardEvent*, bool) 7711.0ms 100.0% 0.0 WebCore::FocusController::advanceFocusInDocumentOrder(WebCore::FocusDirection, WebCore::KeyboardEvent*, bool) 7711.0ms 100.0% 0.0 WebCore::FocusController::findFocusableElementAcrossFocusScope(WebCore::FocusDirection, WebCore::FocusNavigationScope, WebCore::Node*, WebCore::KeyboardEvent*) 7711.0ms 100.0% 0.0 WebCore::FocusController::findFocusableElementRecursively(WebCore::FocusDirection, WebCore::FocusNavigationScope, WebCore::Node*, WebCore::KeyboardEvent*) 7711.0ms 100.0% 0.0 WebCore::FocusController::nextFocusableElement(WebCore::FocusNavigationScope, WebCore::Node*, WebCore::KeyboardEvent*) 7711.0ms 100.0% 0.0 WebCore::FocusController::findElementWithExactTabIndex(WebCore::Node*, int, WebCore::KeyboardEvent*, WebCore::FocusDirection) 7711.0ms 100.0% 0.0 WebCore::HTMLAreaElement::isKeyboardFocusable(WebCore::KeyboardEvent*) const 7711.0ms 100.0% 1.0 WebCore::HTMLAreaElement::isFocusable() const 7710.0ms 99.9% 0.0 WebCore::HTMLMapElement::imageElement() 7683.0ms 99.6% 0.0 WebCore::LiveNodeListBase::item(unsigned int) const <rdar://problem/14920728>
Attachments
Work in progress (9.48 KB, patch)
2013-10-03 21:04 PDT, Ryosuke Niwa
no flags
Fixes the bug (11.92 KB, patch)
2013-10-03 22:20 PDT, Ryosuke Niwa
kling: review+
kling: commit-queue-
Ryosuke Niwa
Comment 1 2013-10-03 21:04:01 PDT
Created attachment 213329 [details] Work in progress
Ryosuke Niwa
Comment 2 2013-10-03 22:20:24 PDT
Created attachment 213335 [details] Fixes the bug
Andreas Kling
Comment 3 2013-10-04 00:10:59 PDT
Comment on attachment 213335 [details] Fixes the bug View in context: https://bugs.webkit.org/attachment.cgi?id=213335&action=review r=me with some improvements. > Source/WebCore/dom/Document.h:262 > + void addImageElementByLowercasedUsemap(const AtomicString&, HTMLImageElement*); > + void removeImageElementByLowercasedUsemap(const AtomicString&, HTMLImageElement*); HTMLImageElement* -> HTMLImageElement& since these don't accept null. > Source/WebCore/html/HTMLImageElement.cpp:139 > + const AtomicString& usemap = fastGetAttribute(HTMLNames::usemapAttr); You already have the usemap attribute in the "value" argument variable here, no need to search for it. > Source/WebCore/html/HTMLImageElement.cpp:141 > + m_lowercasedUsemap = usemap.string().substring(1).lower().impl(); Is the .impl() really needed? > Source/WebCore/html/HTMLImageElement.cpp:324 > + ASSERT(const_cast<AtomicStringImpl*>(&name)->lower() == &name); This would look ~1% nicer as: ASSERT(const_cast<AtomicStringImpl&>(name).lower() == &name); > Source/WebCore/html/HTMLMapElement.cpp:82 > + return document().imageElementByUsemap(m_name.lower()); You could make this even faster by caching a lowercase version of the map's name in a member variable. I don't know what your test case is, so I don't know if it'd be worth much.
Ryosuke Niwa
Comment 4 2013-10-04 16:32:07 PDT
Ryosuke Niwa
Comment 5 2013-10-04 23:54:25 PDT
Fixed intermittent assertion failures reported by ap in http://trac.webkit.org/changeset/156950. e.g. http://build.webkit.org/results/Apple%20Lion%20Debug%20WK2%20(Tests)/r156929%20(11902)/fast/images/imagemap-focus-ring-outline-color-explicitly-inherited-from-map-crash-log.txt CRASHING TEST: fast/images/imagemap-dynamic-area-updates.html Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 com.apple.JavaScriptCore 0x00000001077f9a6a WTFCrash + 42 (Assertions.cpp:342) 1 com.apple.WebCore 0x0000000108b7d2ba WebCore::Element* WebCore::DocumentOrderedMap::get<&(WebCore::keyMatchesLowercasedUsemap(WTF::AtomicStringImpl const*, WebCore::Element*))>(WTF::AtomicStringImpl const*, WebCore::TreeScope const*) const + 554 (DocumentOrderedMap.cpp:155) 2 com.apple.WebCore 0x0000000108b7c105 WebCore::DocumentOrderedMap::getElementByLowercasedUsemap(WTF::AtomicStringImpl const*, WebCore::TreeScope const*) const + 37 (DocumentOrderedMap.cpp:181) 3 com.apple.WebCore 0x0000000108b195e1 WebCore::Document::imageElementByLowercasedUsemap(WTF::AtomicString const&) const + 81 (Document.cpp:722) 4 com.apple.WebCore 0x0000000108f85ff7 WebCore::HTMLMapElement::imageElement() + 71 (HTMLMapElement.cpp:82) 5 com.apple.WebCore 0x0000000108f029b6 WebCore::HTMLAreaElement::imageElement() const + 86 (HTMLAreaElement.cpp:190) 6 com.apple.WebCore 0x0000000108f02a19 WebCore::HTMLAreaElement::isFocusable() const + 25 (HTMLAreaElement.cpp:205) 7 com.apple.WebCore 0x0000000108b17393 WebCore::Document::resetHiddenFocusElementTimer(WebCore::Timer<WebCore::Document>*) + 147 (Document.cpp:4731) 8 com.apple.WebCore 0x0000000108b54cb3 WebCore::Timer<WebCore::Document>::fired() + 115 (Timer.h:114) 9 com.apple.WebCore 0x0000000109f6f4e3 WebCore::ThreadTimers::sharedTimerFiredInternal() + 307 (ThreadTimers.cpp:132) 10 com.apple.WebCore 0x0000000109f6f1f9 WebCore::ThreadTimers::sharedTimerFired() + 25 (ThreadTimers.cpp:106) 11 com.apple.WebCore 0x0000000109d0da73 _ZN7WebCoreL10timerFiredEP16__CFRunLoopTimerPv + 67 (SharedTimerMac.mm:134) 12 com.apple.CoreFoundation 0x00007fff8dc0c934 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20 13 com.apple.CoreFoundation 0x00007fff8dc0c486 __CFRunLoopDoTimer + 534 14 com.apple.CoreFoundation 0x00007fff8dbece11 __CFRunLoopRun + 1617 15 com.apple.CoreFoundation 0x00007fff8dbec486 CFRunLoopRunSpecific + 230 16 com.apple.HIToolbox 0x00007fff988f82bf RunCurrentEventLoopInMode + 277 17 com.apple.HIToolbox 0x00007fff988ff56d ReceiveNextEventCommon + 355 18 com.apple.HIToolbox 0x00007fff988ff3fa BlockUntilNextEventMatchingListInMode + 62 19 com.apple.AppKit 0x00007fff8c450779 _DPSNextEvent + 659 20 com.apple.AppKit 0x00007fff8c45007d -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 135 21 com.apple.AppKit 0x00007fff8c44c9b9 -[NSApplication run] + 470 22 com.apple.WebCore 0x0000000109c5a9b2 WebCore::RunLoop::run() + 114 (RunLoopMac.mm:44) 23 com.apple.WebKit2 0x0000000105cc12bc int WebKit::ChildProcessMain<WebKit::WebProcess, WebKit::WebContentProcessMainDelegate>(int, char**) + 604 (ChildProcessEntryPoint.h:92) 24 com.apple.WebKit2 0x0000000105cc104b WebContentProcessMain + 27 (WebContentProcessMain.mm:179) 25 com.apple.WebProcess 0x0000000105980acd _ZN6WebKitL13BootstrapMainEiPPc + 381 26 com.apple.WebProcess 0x0000000105980942 main + 34 27 com.apple.WebProcess 0x0000000105980914 start + 52
Alexey Proskuryakov
Comment 6 2013-10-05 08:54:43 PDT
Looks like the follow-up fix broke a test on perfbot. It's quite surprising, but it already failed three times in a row after this commit. Running Bindings/typed-array-construct-from-same-type.html (20 of 115) error: Bindings/typed-array-construct-from-same-type.html 2013-10-05 05:24:44.800 DumpRenderTree[15052:707] CoreText CopyPropertiesForFontsMatchingRequest received mig IPC error (FFFFFFFFFFFFFECC) from font server FAILED Finished: 9.988322 s
Ryosuke Niwa
Comment 7 2013-10-05 10:38:37 PDT
(In reply to comment #6) > Looks like the follow-up fix broke a test on perfbot. It's quite surprising, but it already failed three times in a row after this commit. > > Running Bindings/typed-array-construct-from-same-type.html (20 of 115) > error: Bindings/typed-array-construct-from-same-type.html > 2013-10-05 05:24:44.800 DumpRenderTree[15052:707] CoreText CopyPropertiesForFontsMatchingRequest received mig IPC error (FFFFFFFFFFFFFECC) from font server > FAILED > Finished: 9.988322 s On which bot are you seeing this failure? http://build.webkit.org/waterfall?show=Apple%20Lion%20Release%20%28Perf%29&show=Apple%20MountainLion%20Release%20%28Perf%29 looks green.
Alexey Proskuryakov
Comment 8 2013-10-07 09:55:42 PDT
I must have been confused. I no longer see that happening repeatedly.
Note You need to log in before you can comment on or make changes to this bug.