summaryrefslogtreecommitdiff
path: root/Libraries/LibWeb
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-08-02 16:05:59 +0200
committerAndreas Kling <kling@serenityos.org>2020-08-02 17:34:50 +0200
commit07e13e9868b933c5d51271c5e483724485da36fe (patch)
tree6784d8a9cbdd88f35e20211a3a11f656cb3b60b7 /Libraries/LibWeb
parent8b16c61ff879d24e9dc32881ba5bb608f6a35502 (diff)
downloadserenity-07e13e9868b933c5d51271c5e483724485da36fe.zip
LibWeb: Only allow editing of elements with contenteditable="true"
We now respect the contenteditable HTML attribute and only let you edit content inside explicitly editable elements.
Diffstat (limited to 'Libraries/LibWeb')
-rw-r--r--Libraries/LibWeb/DOM/AttributeNames.h91
-rw-r--r--Libraries/LibWeb/DOM/Document.h4
-rw-r--r--Libraries/LibWeb/DOM/Element.cpp18
-rw-r--r--Libraries/LibWeb/DOM/Element.h2
-rw-r--r--Libraries/LibWeb/DOM/Node.cpp5
-rw-r--r--Libraries/LibWeb/DOM/Node.h2
-rw-r--r--Libraries/LibWeb/Layout/LayoutText.cpp3
-rw-r--r--Libraries/LibWeb/Page/EventHandler.cpp50
8 files changed, 106 insertions, 69 deletions
diff --git a/Libraries/LibWeb/DOM/AttributeNames.h b/Libraries/LibWeb/DOM/AttributeNames.h
index 4f45cde8c6..5a1b5fcdde 100644
--- a/Libraries/LibWeb/DOM/AttributeNames.h
+++ b/Libraries/LibWeb/DOM/AttributeNames.h
@@ -34,51 +34,52 @@ namespace AttributeNames {
void initialize();
-#define ENUMERATE_HTML_ATTRIBUTES \
- __ENUMERATE_HTML_ATTRIBUTE(abbr) \
- __ENUMERATE_HTML_ATTRIBUTE(accept) \
- __ENUMERATE_HTML_ATTRIBUTE(action) \
- __ENUMERATE_HTML_ATTRIBUTE(align) \
- __ENUMERATE_HTML_ATTRIBUTE(allow) \
- __ENUMERATE_HTML_ATTRIBUTE(alt) \
- __ENUMERATE_HTML_ATTRIBUTE(async) \
- __ENUMERATE_HTML_ATTRIBUTE(bgcolor) \
- __ENUMERATE_HTML_ATTRIBUTE(class_) \
- __ENUMERATE_HTML_ATTRIBUTE(colspan) \
- __ENUMERATE_HTML_ATTRIBUTE(data) \
- __ENUMERATE_HTML_ATTRIBUTE(download) \
- __ENUMERATE_HTML_ATTRIBUTE(defer) \
- __ENUMERATE_HTML_ATTRIBUTE(dirname) \
- __ENUMERATE_HTML_ATTRIBUTE(headers) \
- __ENUMERATE_HTML_ATTRIBUTE(height) \
- __ENUMERATE_HTML_ATTRIBUTE(href) \
- __ENUMERATE_HTML_ATTRIBUTE(hreflang) \
- __ENUMERATE_HTML_ATTRIBUTE(id) \
- __ENUMERATE_HTML_ATTRIBUTE(imagesizes) \
- __ENUMERATE_HTML_ATTRIBUTE(imagesrcset) \
- __ENUMERATE_HTML_ATTRIBUTE(integrity) \
- __ENUMERATE_HTML_ATTRIBUTE(lang) \
- __ENUMERATE_HTML_ATTRIBUTE(max) \
- __ENUMERATE_HTML_ATTRIBUTE(media) \
- __ENUMERATE_HTML_ATTRIBUTE(method) \
- __ENUMERATE_HTML_ATTRIBUTE(min) \
- __ENUMERATE_HTML_ATTRIBUTE(name) \
- __ENUMERATE_HTML_ATTRIBUTE(pattern) \
- __ENUMERATE_HTML_ATTRIBUTE(ping) \
- __ENUMERATE_HTML_ATTRIBUTE(placeholder) \
- __ENUMERATE_HTML_ATTRIBUTE(rel) \
- __ENUMERATE_HTML_ATTRIBUTE(size) \
- __ENUMERATE_HTML_ATTRIBUTE(sizes) \
- __ENUMERATE_HTML_ATTRIBUTE(src) \
- __ENUMERATE_HTML_ATTRIBUTE(srcdoc) \
- __ENUMERATE_HTML_ATTRIBUTE(srcset) \
- __ENUMERATE_HTML_ATTRIBUTE(step) \
- __ENUMERATE_HTML_ATTRIBUTE(style) \
- __ENUMERATE_HTML_ATTRIBUTE(target) \
- __ENUMERATE_HTML_ATTRIBUTE(title) \
- __ENUMERATE_HTML_ATTRIBUTE(type) \
- __ENUMERATE_HTML_ATTRIBUTE(usemap) \
- __ENUMERATE_HTML_ATTRIBUTE(value) \
+#define ENUMERATE_HTML_ATTRIBUTES \
+ __ENUMERATE_HTML_ATTRIBUTE(abbr) \
+ __ENUMERATE_HTML_ATTRIBUTE(accept) \
+ __ENUMERATE_HTML_ATTRIBUTE(action) \
+ __ENUMERATE_HTML_ATTRIBUTE(align) \
+ __ENUMERATE_HTML_ATTRIBUTE(allow) \
+ __ENUMERATE_HTML_ATTRIBUTE(alt) \
+ __ENUMERATE_HTML_ATTRIBUTE(async) \
+ __ENUMERATE_HTML_ATTRIBUTE(bgcolor) \
+ __ENUMERATE_HTML_ATTRIBUTE(class_) \
+ __ENUMERATE_HTML_ATTRIBUTE(colspan) \
+ __ENUMERATE_HTML_ATTRIBUTE(contenteditable) \
+ __ENUMERATE_HTML_ATTRIBUTE(data) \
+ __ENUMERATE_HTML_ATTRIBUTE(download) \
+ __ENUMERATE_HTML_ATTRIBUTE(defer) \
+ __ENUMERATE_HTML_ATTRIBUTE(dirname) \
+ __ENUMERATE_HTML_ATTRIBUTE(headers) \
+ __ENUMERATE_HTML_ATTRIBUTE(height) \
+ __ENUMERATE_HTML_ATTRIBUTE(href) \
+ __ENUMERATE_HTML_ATTRIBUTE(hreflang) \
+ __ENUMERATE_HTML_ATTRIBUTE(id) \
+ __ENUMERATE_HTML_ATTRIBUTE(imagesizes) \
+ __ENUMERATE_HTML_ATTRIBUTE(imagesrcset) \
+ __ENUMERATE_HTML_ATTRIBUTE(integrity) \
+ __ENUMERATE_HTML_ATTRIBUTE(lang) \
+ __ENUMERATE_HTML_ATTRIBUTE(max) \
+ __ENUMERATE_HTML_ATTRIBUTE(media) \
+ __ENUMERATE_HTML_ATTRIBUTE(method) \
+ __ENUMERATE_HTML_ATTRIBUTE(min) \
+ __ENUMERATE_HTML_ATTRIBUTE(name) \
+ __ENUMERATE_HTML_ATTRIBUTE(pattern) \
+ __ENUMERATE_HTML_ATTRIBUTE(ping) \
+ __ENUMERATE_HTML_ATTRIBUTE(placeholder) \
+ __ENUMERATE_HTML_ATTRIBUTE(rel) \
+ __ENUMERATE_HTML_ATTRIBUTE(size) \
+ __ENUMERATE_HTML_ATTRIBUTE(sizes) \
+ __ENUMERATE_HTML_ATTRIBUTE(src) \
+ __ENUMERATE_HTML_ATTRIBUTE(srcdoc) \
+ __ENUMERATE_HTML_ATTRIBUTE(srcset) \
+ __ENUMERATE_HTML_ATTRIBUTE(step) \
+ __ENUMERATE_HTML_ATTRIBUTE(style) \
+ __ENUMERATE_HTML_ATTRIBUTE(target) \
+ __ENUMERATE_HTML_ATTRIBUTE(title) \
+ __ENUMERATE_HTML_ATTRIBUTE(type) \
+ __ENUMERATE_HTML_ATTRIBUTE(usemap) \
+ __ENUMERATE_HTML_ATTRIBUTE(value) \
__ENUMERATE_HTML_ATTRIBUTE(width)
#define __ENUMERATE_HTML_ATTRIBUTE(name) extern FlyString name;
diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h
index 3f8bfe7cf1..6997e1b493 100644
--- a/Libraries/LibWeb/DOM/Document.h
+++ b/Libraries/LibWeb/DOM/Document.h
@@ -157,6 +157,9 @@ public:
const DocumentType* doctype() const;
const String& compat_mode() const;
+ void set_editable(bool editable) { m_editable = editable; }
+ virtual bool is_editable() const final;
+
private:
virtual RefPtr<LayoutNode> create_layout_node(const CSS::StyleProperties* parent_style) override;
@@ -186,6 +189,7 @@ private:
NonnullRefPtrVector<HTML::HTMLScriptElement> m_scripts_to_execute_as_soon_as_possible;
QuirksMode m_quirks_mode { QuirksMode::No };
+ bool m_editable { false };
};
}
diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp
index 78a2fc7120..6c8b08b47d 100644
--- a/Libraries/LibWeb/DOM/Element.cpp
+++ b/Libraries/LibWeb/DOM/Element.cpp
@@ -290,4 +290,22 @@ String Element::inner_html() const
return builder.to_string();
}
+bool Element::is_editable() const
+{
+ auto contenteditable = attribute(HTML::AttributeNames::contenteditable);
+ // "true" and the empty string map to the "true" state.
+ if ((!contenteditable.is_null() && contenteditable.is_empty()) || contenteditable.equals_ignoring_case("true"))
+ return true;
+ // "false" maps to the "false" state.
+ if (contenteditable.equals_ignoring_case("false"))
+ return false;
+ // "inherit", an invalid value, and a missing value all map to the "inherit" state.
+ return parent() && parent()->is_editable();
+}
+
+bool Document::is_editable() const
+{
+ return m_editable;
+}
+
}
diff --git a/Libraries/LibWeb/DOM/Element.h b/Libraries/LibWeb/DOM/Element.h
index 579bee58ba..937db4382b 100644
--- a/Libraries/LibWeb/DOM/Element.h
+++ b/Libraries/LibWeb/DOM/Element.h
@@ -82,6 +82,8 @@ public:
String inner_html() const;
void set_inner_html(StringView);
+ virtual bool is_editable() const final;
+
protected:
RefPtr<LayoutNode> create_layout_node(const CSS::StyleProperties* parent_style) override;
diff --git a/Libraries/LibWeb/DOM/Node.cpp b/Libraries/LibWeb/DOM/Node.cpp
index 744c0f27d2..4370a540c6 100644
--- a/Libraries/LibWeb/DOM/Node.cpp
+++ b/Libraries/LibWeb/DOM/Node.cpp
@@ -217,4 +217,9 @@ void Node::set_document(Badge<Document>, Document& document)
m_document = &document;
}
+bool Node::is_editable() const
+{
+ return parent() && parent()->is_editable();
+}
+
}
diff --git a/Libraries/LibWeb/DOM/Node.h b/Libraries/LibWeb/DOM/Node.h
index cc73a7ac67..1bf2ab9d9b 100644
--- a/Libraries/LibWeb/DOM/Node.h
+++ b/Libraries/LibWeb/DOM/Node.h
@@ -75,6 +75,8 @@ public:
bool is_parent_node() const { return is_element() || is_document() || is_document_fragment(); }
virtual bool is_svg_element() const { return false; }
+ virtual bool is_editable() const;
+
RefPtr<Node> append_child(NonnullRefPtr<Node>, bool notify = true);
RefPtr<Node> insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool notify = true);
diff --git a/Libraries/LibWeb/Layout/LayoutText.cpp b/Libraries/LibWeb/Layout/LayoutText.cpp
index 55696b5531..61839b4bb1 100644
--- a/Libraries/LibWeb/Layout/LayoutText.cpp
+++ b/Libraries/LibWeb/Layout/LayoutText.cpp
@@ -117,6 +117,9 @@ void LayoutText::paint_cursor_if_needed(PaintContext& context, const LineBoxFrag
if (!(frame().cursor_position().offset() >= (unsigned)fragment.start() && frame().cursor_position().offset() < (unsigned)(fragment.start() + fragment.length())))
return;
+ if (!fragment.layout_node().node() || !fragment.layout_node().node()->is_editable())
+ return;
+
auto fragment_rect = fragment.absolute_rect();
float cursor_x = fragment_rect.x() + specified_style().font().width(fragment.text().substring_view(0, frame().cursor_position().offset() - fragment.start()));
diff --git a/Libraries/LibWeb/Page/EventHandler.cpp b/Libraries/LibWeb/Page/EventHandler.cpp
index bf03ab46e3..76e6c3badb 100644
--- a/Libraries/LibWeb/Page/EventHandler.cpp
+++ b/Libraries/LibWeb/Page/EventHandler.cpp
@@ -231,31 +231,33 @@ void EventHandler::dump_selection(const char* event_name) const
bool EventHandler::handle_keydown(KeyCode key, unsigned, u32 code_point)
{
- // FIXME: Support backspacing across DOM node boundaries.
- if (key == KeyCode::Key_Backspace && m_frame.cursor_position().offset() > 0) {
- auto& text_node = downcast<DOM::Text>(*m_frame.cursor_position().node());
- StringBuilder builder;
- builder.append(text_node.data().substring_view(0, m_frame.cursor_position().offset() - 1));
- builder.append(text_node.data().substring_view(m_frame.cursor_position().offset(), text_node.data().length() - m_frame.cursor_position().offset()));
- text_node.set_data(builder.to_string());
- m_frame.set_cursor_position({ *m_frame.cursor_position().node(), m_frame.cursor_position().offset() - 1 });
- // FIXME: This should definitely use incremental layout invalidation instead!
- text_node.document().force_layout();
- return true;
- }
+ if (m_frame.cursor_position().node() && m_frame.cursor_position().node()->is_editable()) {
+ // FIXME: Support backspacing across DOM node boundaries.
+ if (key == KeyCode::Key_Backspace && m_frame.cursor_position().offset() > 0) {
+ auto& text_node = downcast<DOM::Text>(*m_frame.cursor_position().node());
+ StringBuilder builder;
+ builder.append(text_node.data().substring_view(0, m_frame.cursor_position().offset() - 1));
+ builder.append(text_node.data().substring_view(m_frame.cursor_position().offset(), text_node.data().length() - m_frame.cursor_position().offset()));
+ text_node.set_data(builder.to_string());
+ m_frame.set_cursor_position({ *m_frame.cursor_position().node(), m_frame.cursor_position().offset() - 1 });
+ // FIXME: This should definitely use incremental layout invalidation instead!
+ text_node.document().force_layout();
+ return true;
+ }
- if (code_point && m_frame.cursor_position().is_valid() && is<DOM::Text>(*m_frame.cursor_position().node())) {
- auto& text_node = downcast<DOM::Text>(*m_frame.cursor_position().node());
- StringBuilder builder;
- builder.append(text_node.data().substring_view(0, m_frame.cursor_position().offset()));
- builder.append_codepoint(code_point);
- builder.append(text_node.data().substring_view(m_frame.cursor_position().offset(), text_node.data().length() - m_frame.cursor_position().offset()));
- text_node.set_data(builder.to_string());
- // FIXME: This will advance the cursor incorrectly when inserting multiple whitespaces (DOM vs layout whitespace collapse difference.)
- m_frame.set_cursor_position({ *m_frame.cursor_position().node(), m_frame.cursor_position().offset() + 1 });
- // FIXME: This should definitely use incremental layout invalidation instead!
- text_node.document().force_layout();
- return true;
+ if (code_point && m_frame.cursor_position().is_valid() && is<DOM::Text>(*m_frame.cursor_position().node())) {
+ auto& text_node = downcast<DOM::Text>(*m_frame.cursor_position().node());
+ StringBuilder builder;
+ builder.append(text_node.data().substring_view(0, m_frame.cursor_position().offset()));
+ builder.append_codepoint(code_point);
+ builder.append(text_node.data().substring_view(m_frame.cursor_position().offset(), text_node.data().length() - m_frame.cursor_position().offset()));
+ text_node.set_data(builder.to_string());
+ // FIXME: This will advance the cursor incorrectly when inserting multiple whitespaces (DOM vs layout whitespace collapse difference.)
+ m_frame.set_cursor_position({ *m_frame.cursor_position().node(), m_frame.cursor_position().offset() + 1 });
+ // FIXME: This should definitely use incremental layout invalidation instead!
+ text_node.document().force_layout();
+ return true;
+ }
}
return false;
}