summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--third_party/WebKit/LayoutTests/fast/canvas/canvas-hit-regions-fallback-element-test.html91
-rw-r--r--third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp64
-rw-r--r--third_party/WebKit/Source/core/html/HTMLCanvasElement.h2
-rw-r--r--third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp5
4 files changed, 162 insertions, 0 deletions
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-hit-regions-fallback-element-test.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-hit-regions-fallback-element-test.html
new file mode 100644
index 0000000..17e59e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-hit-regions-fallback-element-test.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<title>HitRegion Canvas Fallback Element Test</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="./resources/test-helpers.js"></script>
+<canvas width="400" height="400">
+ <a id="a"></a>
+ <a id="a_with_image"><img></a>
+ <button id="button"></button>
+ <input id="checkbox" type="checkbox">
+ <input id="radio" type="radio">
+ <input id="input_button" type="button">
+ <input id="input_image_button" type="image">
+ <select id="empty_select"></select>
+ <select id="select_size_greater_than_1" size="2"></select>
+ <select id="select_multiple" multiple="multiple"></select>
+ <select>
+ <option id="option_with_select"></option>
+ </select>
+ <select multiple="multiple">
+ <option id="option_with_select_multiple"></option>
+ </select>
+ <select size="2">
+ <option id="option_with_select_size_greater_than_1"></option>
+ </select>
+ <p id="p"></p>
+ <p id="p_with_tabindex" tabindex="0"></p>
+ <table>
+ <caption></caption>
+ <thead><tr><th></th></tr></thead>
+ <tfoot><tr><td></td></tr></tfoot>
+ <tbody><tr><td></td></tr></tbody>
+ </table>
+</canvas>
+<button id="button_is_not_descendant_of_canvas"></button>
+<style>
+
+body {
+ margin : 0px;
+ padding : 0px;
+}
+
+</style>
+<script>
+
+function canvas_fallback_test(element, expected) {
+ test(function() {
+ var canvas = document.querySelector('canvas');
+ var context = canvas.getContext('2d');
+
+ context.clearRect(0, 0, 400, 400);
+ context.rect(0, 0, 100, 100);
+ if (expected) {
+ assert_throws(expected, function() {
+ context.addHitRegion({ control : element });
+ });
+ } else {
+ context.addHitRegion({ control : element });
+ }
+ }, element.id);
+}
+
+const NotSupportedError = { name : 'NotSupportedError' };
+
+canvas_fallback_test(document.getElementById('button_is_not_descendant_of_canvas'), NotSupportedError);
+canvas_fallback_test(document.querySelector('canvas'), NotSupportedError);
+canvas_fallback_test(document.getElementById('a'));
+canvas_fallback_test(document.getElementById('a_with_image'), NotSupportedError);
+canvas_fallback_test(document.getElementById('button'));
+canvas_fallback_test(document.getElementById('checkbox'));
+canvas_fallback_test(document.getElementById('radio'));
+canvas_fallback_test(document.getElementById('input_button'));
+canvas_fallback_test(document.getElementById('input_image_button'), NotSupportedError);
+canvas_fallback_test(document.getElementById('empty_select'), NotSupportedError);
+canvas_fallback_test(document.getElementById('select_size_greater_than_1'));
+canvas_fallback_test(document.getElementById('select_multiple'));
+canvas_fallback_test(document.getElementById('option_with_select'), NotSupportedError);
+canvas_fallback_test(document.getElementById('option_with_select_multiple'));
+canvas_fallback_test(document.getElementById('option_with_select_size_greater_than_1'));
+canvas_fallback_test(document.getElementById('p'), NotSupportedError);
+canvas_fallback_test(document.getElementById('p_with_tabindex'));
+canvas_fallback_test(document.querySelector('canvas table'));
+canvas_fallback_test(document.querySelector('canvas tr'));
+canvas_fallback_test(document.querySelector('canvas th'));
+canvas_fallback_test(document.querySelector('canvas td'));
+canvas_fallback_test(document.querySelector('canvas thead'));
+canvas_fallback_test(document.querySelector('canvas tfoot'));
+canvas_fallback_test(document.querySelector('canvas tbody'));
+canvas_fallback_test(document.querySelector('canvas caption'));
+
+</script>
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
index a7f4745..40f436f 100644
--- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
@@ -31,12 +31,18 @@
#include "bindings/core/v8/ExceptionState.h"
#include "bindings/core/v8/ScriptController.h"
#include "core/HTMLNames.h"
+#include "core/InputTypeNames.h"
#include "core/dom/Document.h"
+#include "core/dom/Element.h"
+#include "core/dom/ElementTraversal.h"
#include "core/dom/ExceptionCode.h"
#include "core/fileapi/File.h"
#include "core/frame/ImageBitmap.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/Settings.h"
+#include "core/html/HTMLImageElement.h"
+#include "core/html/HTMLInputElement.h"
+#include "core/html/HTMLSelectElement.h"
#include "core/html/ImageData.h"
#include "core/html/canvas/CanvasAsyncBlobCreator.h"
#include "core/html/canvas/CanvasContextCreationAttributes.h"
@@ -1020,4 +1026,62 @@ bool HTMLCanvasElement::isOpaque() const
return m_context && !m_context->hasAlpha();
}
+bool HTMLCanvasElement::isSupportedInteractiveCanvasFallback(const Element& element)
+{
+ if (!element.isDescendantOf(this))
+ return false;
+
+ // An element is a supported interactive canvas fallback element if it is one of the following:
+ // https://html.spec.whatwg.org/multipage/scripting.html#supported-interactive-canvas-fallback-element
+
+ // An a element that represents a hyperlink and that does not have any img descendants.
+ if (isHTMLAnchorElement(element))
+ return !Traversal<HTMLImageElement>::firstWithin(element);
+
+ // A button element
+ if (isHTMLButtonElement(element))
+ return true;
+
+ // An input element whose type attribute is in one of the Checkbox or Radio Button states.
+ // An input element that is a button but its type attribute is not in the Image Button state.
+ if (isHTMLInputElement(element)) {
+ const HTMLInputElement& inputElement = toHTMLInputElement(element);
+ if (inputElement.type() == InputTypeNames::checkbox
+ || inputElement.type() == InputTypeNames::radio
+ || inputElement.isTextButton())
+ return true;
+ }
+
+ // A select element with a multiple attribute or a display size greater than 1.
+ if (isHTMLSelectElement(element)) {
+ const HTMLSelectElement& selectElement = toHTMLSelectElement(element);
+ if (selectElement.multiple() || selectElement.size() > 1)
+ return true;
+ }
+
+ // An option element that is in a list of options of a select element with a multiple attribute or a display size greater than 1.
+ if (isHTMLOptionElement(element) && element.parentNode() && isHTMLSelectElement(*element.parentNode())) {
+ const HTMLSelectElement& selectElement = toHTMLSelectElement(*element.parentNode());
+ if (selectElement.multiple() || selectElement.size() > 1)
+ return true;
+ }
+
+ // An element that would not be interactive content except for having the tabindex attribute specified.
+ if (element.fastHasAttribute(HTMLNames::tabindexAttr))
+ return true;
+
+ // A non-interactive table, caption, thead, tbody, tfoot, tr, td, or th element.
+ if (isHTMLTableElement(element)
+ || element.hasTagName(HTMLNames::captionTag)
+ || element.hasTagName(HTMLNames::theadTag)
+ || element.hasTagName(HTMLNames::tbodyTag)
+ || element.hasTagName(HTMLNames::tfootTag)
+ || element.hasTagName(HTMLNames::trTag)
+ || element.hasTagName(HTMLNames::tdTag)
+ || element.hasTagName(HTMLNames::thTag))
+ return true;
+
+ return false;
+}
+
} // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.h b/third_party/WebKit/Source/core/html/HTMLCanvasElement.h
index da30a88..5d7d9d9 100644
--- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.h
@@ -181,6 +181,8 @@ public:
void notifyListenersCanvasChanged();
+ bool isSupportedInteractiveCanvasFallback(const Element&);
+
protected:
void didMoveToNewDocument(Document& oldDocument) override;
diff --git a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp
index bd7f158..7fe1240 100644
--- a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp
+++ b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp
@@ -2234,6 +2234,11 @@ void CanvasRenderingContext2D::addHitRegion(const HitRegionOptions& options, Exc
return;
}
+ if (options.control() && !canvas()->isSupportedInteractiveCanvasFallback(*options.control())) {
+ exceptionState.throwDOMException(NotSupportedError, "The control is neither null nor a supported interactive canvas fallback element.");
+ return;
+ }
+
Path hitRegionPath = options.hasPath() ? options.path()->path() : m_path;
SkCanvas* c = drawingCanvas();