 Chromium Code Reviews
 Chromium Code Reviews Issue 166163005:
  [SVG2] Add tabindex handling in svg.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/blink.git@master
    
  
    Issue 166163005:
  [SVG2] Add tabindex handling in svg.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/blink.git@master| OLD | NEW | 
|---|---|
| 1 /* | 1 /* | 
| 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde .org> | 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde .org> | 
| 3 * Copyright (C) 2004, 2005, 2006, 2008 Rob Buis <buis@kde.org> | 3 * Copyright (C) 2004, 2005, 2006, 2008 Rob Buis <buis@kde.org> | 
| 4 * Copyright (C) 2008 Apple Inc. All rights reserved. | 4 * Copyright (C) 2008 Apple Inc. All rights reserved. | 
| 5 * Copyright (C) 2008 Alp Toker <alp@atoker.com> | 5 * Copyright (C) 2008 Alp Toker <alp@atoker.com> | 
| 6 * Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au> | 6 * Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au> | 
| 7 * | 7 * | 
| 8 * This library is free software; you can redistribute it and/or | 8 * This library is free software; you can redistribute it and/or | 
| 9 * modify it under the terms of the GNU Library General Public | 9 * modify it under the terms of the GNU Library General Public | 
| 10 * License as published by the Free Software Foundation; either | 10 * License as published by the Free Software Foundation; either | 
| (...skipping 18 matching lines...) Expand all Loading... | |
| 29 #include "SVGNames.h" | 29 #include "SVGNames.h" | 
| 30 #include "XLinkNames.h" | 30 #include "XLinkNames.h" | 
| 31 #include "XMLNames.h" | 31 #include "XMLNames.h" | 
| 32 #include "bindings/v8/ScriptEventListener.h" | 32 #include "bindings/v8/ScriptEventListener.h" | 
| 33 #include "core/css/CSSCursorImageValue.h" | 33 #include "core/css/CSSCursorImageValue.h" | 
| 34 #include "core/css/parser/BisonCSSParser.h" | 34 #include "core/css/parser/BisonCSSParser.h" | 
| 35 #include "core/dom/Document.h" | 35 #include "core/dom/Document.h" | 
| 36 #include "core/dom/ElementTraversal.h" | 36 #include "core/dom/ElementTraversal.h" | 
| 37 #include "core/dom/shadow/ShadowRoot.h" | 37 #include "core/dom/shadow/ShadowRoot.h" | 
| 38 #include "core/events/Event.h" | 38 #include "core/events/Event.h" | 
| 39 #include "core/frame/Settings.h" | |
| 39 #include "core/html/HTMLElement.h" | 40 #include "core/html/HTMLElement.h" | 
| 41 #include "core/html/parser/HTMLParserIdioms.h" | |
| 40 #include "core/rendering/RenderObject.h" | 42 #include "core/rendering/RenderObject.h" | 
| 41 #include "core/rendering/svg/RenderSVGResourceContainer.h" | 43 #include "core/rendering/svg/RenderSVGResourceContainer.h" | 
| 42 #include "core/svg/SVGCursorElement.h" | 44 #include "core/svg/SVGCursorElement.h" | 
| 43 #include "core/svg/SVGDocumentExtensions.h" | 45 #include "core/svg/SVGDocumentExtensions.h" | 
| 44 #include "core/svg/SVGElementInstance.h" | 46 #include "core/svg/SVGElementInstance.h" | 
| 45 #include "core/svg/SVGElementRareData.h" | 47 #include "core/svg/SVGElementRareData.h" | 
| 46 #include "core/svg/SVGGraphicsElement.h" | 48 #include "core/svg/SVGGraphicsElement.h" | 
| 47 #include "core/svg/SVGSVGElement.h" | 49 #include "core/svg/SVGSVGElement.h" | 
| 48 #include "core/svg/SVGTitleElement.h" | 50 #include "core/svg/SVGTitleElement.h" | 
| 49 #include "core/svg/SVGUseElement.h" | 51 #include "core/svg/SVGUseElement.h" | 
| (...skipping 573 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 623 { | 625 { | 
| 624 ASSERT(!hasSVGRareData() || !svgRareData()->correspondingElement() || contai ningShadowRoot()); | 626 ASSERT(!hasSVGRareData() || !svgRareData()->correspondingElement() || contai ningShadowRoot()); | 
| 625 return hasSVGRareData() ? svgRareData()->correspondingElement() : 0; | 627 return hasSVGRareData() ? svgRareData()->correspondingElement() : 0; | 
| 626 } | 628 } | 
| 627 | 629 | 
| 628 void SVGElement::setCorrespondingElement(SVGElement* correspondingElement) | 630 void SVGElement::setCorrespondingElement(SVGElement* correspondingElement) | 
| 629 { | 631 { | 
| 630 ensureSVGRareData()->setCorrespondingElement(correspondingElement); | 632 ensureSVGRareData()->setCorrespondingElement(correspondingElement); | 
| 631 } | 633 } | 
| 632 | 634 | 
| 635 short SVGElement::tabIndex() const | |
| 636 { | |
| 637 if (supportsFocus()) | |
| 638 return Element::tabIndex(); | |
| 639 return -1; | |
| 640 } | |
| 641 | |
| 642 void SVGElement::setTabIndex(int value) | |
| 643 { | |
| 644 setIntegralAttribute(tabindexAttr, value); | |
| 645 } | |
| 646 | |
| 647 bool SVGElement::supportsFocus() const | |
| 
pdr.
2014/03/24 16:29:54
There's a lot of duplicate and non-obvious logic f
 
Erik Dahlström (inactive)
2014/03/25 08:24:03
I'll try moving it up to Element, np.
 | |
| 648 { | |
| 649 // FIXME: supportsFocus() can be called when layout is not up to date. | |
| 650 // Logic that deals with the renderer should be moved to rendererIsFocusable (). | |
| 651 // But supportsFocus must return true when the element is editable, or else | |
| 652 // it won't be focusable. Furthermore, supportsFocus cannot just return true | |
| 653 // always or else tabIndex() will change for all HTML elements. | |
| 654 return Element::supportsFocus() || (rendererIsEditable() && parentNode() && !parentNode()->rendererIsEditable()) | |
| 655 || supportsSpatialNavigationFocus(); | |
| 656 } | |
| 657 | |
| 658 bool SVGElement::supportsSpatialNavigationFocus() const | |
| 659 { | |
| 660 // This function checks whether the element satisfies the extended criteria | |
| 661 // for the element to be focusable, introduced by spatial navigation feature , | |
| 662 // i.e. checks if click or keyboard event handler is specified. | |
| 663 // This is the way to make it possible to navigate to (focus) elements | |
| 664 // which web designer meant for being active (made them respond to click eve nts). | |
| 665 | |
| 666 if (!document().settings() || !document().settings()->spatialNavigationEnabl ed()) | |
| 667 return false; | |
| 668 return hasEventListeners(EventTypeNames::click) | |
| 669 || hasEventListeners(EventTypeNames::keydown) | |
| 670 || hasEventListeners(EventTypeNames::keypress) | |
| 671 || hasEventListeners(EventTypeNames::keyup) | |
| 672 || hasEventListeners(EventTypeNames::focus) | |
| 673 || hasEventListeners(EventTypeNames::blur) | |
| 674 || hasEventListeners(EventTypeNames::focusin) | |
| 675 || hasEventListeners(EventTypeNames::focusout); | |
| 
Stephen Chennney
2014/03/24 17:43:50
Is this set spec defined? Do we need touch events
 
Erik Dahlström (inactive)
2014/03/25 08:24:03
It's not spec-defined no. It is mostly based on wh
 | |
| 676 } | |
| 677 | |
| 633 void SVGElement::parseAttribute(const QualifiedName& name, const AtomicString& v alue) | 678 void SVGElement::parseAttribute(const QualifiedName& name, const AtomicString& v alue) | 
| 634 { | 679 { | 
| 635 if (name == HTMLNames::classAttr) { | 680 if (name == HTMLNames::classAttr) { | 
| 636 // SVG animation has currently requires special storage of values so we set | 681 // SVG animation has currently requires special storage of values so we set | 
| 637 // the className here. svgAttributeChanged actually causes the resulting | 682 // the className here. svgAttributeChanged actually causes the resulting | 
| 638 // style updates (instead of Element::parseAttribute). We don't | 683 // style updates (instead of Element::parseAttribute). We don't | 
| 639 // tell Element about the change to avoid parsing the class list twice | 684 // tell Element about the change to avoid parsing the class list twice | 
| 640 SVGParsingError parseError = NoError; | 685 SVGParsingError parseError = NoError; | 
| 641 m_className->setBaseValueAsString(value, parseError); | 686 m_className->setBaseValueAsString(value, parseError); | 
| 642 reportAttributeParsingError(parseError, name, value); | 687 reportAttributeParsingError(parseError, name, value); | 
| 643 } else if (name.matches(XMLNames::langAttr) || name.matches(XMLNames::spaceA ttr)) { | 688 } else if (name.matches(XMLNames::langAttr) || name.matches(XMLNames::spaceA ttr)) { | 
| 689 } else if (name == tabindexAttr) { | |
| 690 int tabindex = 0; | |
| 691 if (value.isEmpty()) { | |
| 692 clearTabIndexExplicitlyIfNeeded(); | |
| 693 if (treeScope().adjustedFocusedElement() == this) { | |
| 694 // We might want to call blur(), but it's dangerous to dispatch | |
| 695 // events here. | |
| 696 document().setNeedsFocusedElementCheck(); | |
| 697 } | |
| 698 } else if (parseHTMLInteger(value, tabindex)) { | |
| 699 // Clamp tabindex to the range of 'short' to match Firefox's behavio r. | |
| 700 setTabIndexExplicitly(max(static_cast<int>(std::numeric_limits<short >::min()), std::min(tabindex, static_cast<int>(std::numeric_limits<short>::max() )))); | |
| 701 } | |
| 644 } else { | 702 } else { | 
| 645 // standard events | 703 // standard events | 
| 646 const AtomicString& eventName = HTMLElement::eventNameForAttributeName(n ame); | 704 const AtomicString& eventName = HTMLElement::eventNameForAttributeName(n ame); | 
| 647 if (!eventName.isNull()) | 705 if (!eventName.isNull()) | 
| 648 setAttributeEventListener(eventName, createAttributeEventListener(th is, name, value)); | 706 setAttributeEventListener(eventName, createAttributeEventListener(th is, name, value)); | 
| 649 else | 707 else | 
| 650 Element::parseAttribute(name, value); | 708 Element::parseAttribute(name, value); | 
| 651 } | 709 } | 
| 652 } | 710 } | 
| 653 | 711 | 
| (...skipping 364 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1018 if (Element* parent = parentOrShadowHostElement()) { | 1076 if (Element* parent = parentOrShadowHostElement()) { | 
| 1019 if (RenderObject* renderer = parent->renderer()) | 1077 if (RenderObject* renderer = parent->renderer()) | 
| 1020 parentStyle = renderer->style(); | 1078 parentStyle = renderer->style(); | 
| 1021 } | 1079 } | 
| 1022 | 1080 | 
| 1023 return svgRareData()->overrideComputedStyle(this, parentStyle); | 1081 return svgRareData()->overrideComputedStyle(this, parentStyle); | 
| 1024 } | 1082 } | 
| 1025 | 1083 | 
| 1026 bool SVGElement::hasFocusEventListeners() const | 1084 bool SVGElement::hasFocusEventListeners() const | 
| 1027 { | 1085 { | 
| 1028 return hasEventListeners(EventTypeNames::focusin) || hasEventListeners(Event TypeNames::focusout); | 1086 return hasEventListeners(EventTypeNames::focusin) || hasEventListeners(Event TypeNames::focusout) | 
| 1029 } | 1087 || hasEventListeners(EventTypeNames::focus) || hasEventListeners(EventTy peNames::blur); | 
| 1030 | |
| 1031 bool SVGElement::isKeyboardFocusable() const | |
| 1032 { | |
| 1033 return isFocusable(); | |
| 1034 } | 1088 } | 
| 1035 | 1089 | 
| 1036 #ifndef NDEBUG | 1090 #ifndef NDEBUG | 
| 1037 bool SVGElement::isAnimatableAttribute(const QualifiedName& name) const | 1091 bool SVGElement::isAnimatableAttribute(const QualifiedName& name) const | 
| 1038 { | 1092 { | 
| 1039 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, animatableAttributes, ()); | 1093 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, animatableAttributes, ()); | 
| 1040 | 1094 | 
| 1041 if (animatableAttributes.isEmpty()) { | 1095 if (animatableAttributes.isEmpty()) { | 
| 1042 animatableAttributes.add(XLinkNames::hrefAttr); | 1096 animatableAttributes.add(XLinkNames::hrefAttr); | 
| 1043 animatableAttributes.add(SVGNames::amplitudeAttr); | 1097 animatableAttributes.add(SVGNames::amplitudeAttr); | 
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1133 animatableAttributes.add(SVGNames::zAttr); | 1187 animatableAttributes.add(SVGNames::zAttr); | 
| 1134 } | 1188 } | 
| 1135 | 1189 | 
| 1136 if (name == classAttr) | 1190 if (name == classAttr) | 
| 1137 return true; | 1191 return true; | 
| 1138 | 1192 | 
| 1139 return animatableAttributes.contains(name); | 1193 return animatableAttributes.contains(name); | 
| 1140 } | 1194 } | 
| 1141 #endif | 1195 #endif | 
| 1142 } | 1196 } | 
| OLD | NEW |