diff --git a/crates/mdbook-html/front-end/js/book.js b/crates/mdbook-html/front-end/js/book.js
index 62d7c4cc..fe74b710 100644
--- a/crates/mdbook-html/front-end/js/book.js
+++ b/crates/mdbook-html/front-end/js/book.js
@@ -19,6 +19,17 @@ function playground_text(playground, hidden = true) {
}
}
+/**
+ * Helper for global keypress handlers so they don't trigger when certain elements are active.
+ * @returns {boolean} True if the keypress handler should be skipped.
+ */
+function mdbook_something_else_has_focus(e) {
+ // Check composedPath in case the event happened from something generated
+ // from the shadowDOM.
+ const target = e.composedPath()[0] || e.target;
+ return /^(?:input|select|textarea)$/i.test(target.nodeName);
+}
+
(function codeSnippets() {
function fetch_with_timeout(url, options, timeout = 6000) {
return Promise.race([
@@ -648,12 +659,15 @@ aria-label="Show hidden lines">';
(function chapterNavigation() {
document.addEventListener('keydown', function(e) {
- if (e.altKey || e.ctrlKey || e.metaKey) {
- return;
- }
- if (window.search && window.search.hasFocus()) {
+ if (e.altKey ||
+ e.ctrlKey ||
+ e.metaKey ||
+ window.search && window.search.hasFocus() ||
+ mdbook_something_else_has_focus(e)
+ ) {
return;
}
+
const html = document.querySelector('html');
function next() {
diff --git a/crates/mdbook-html/front-end/searcher/searcher.js b/crates/mdbook-html/front-end/searcher/searcher.js
index 5c4bab65..ea17ae38 100644
--- a/crates/mdbook-html/front-end/searcher/searcher.js
+++ b/crates/mdbook-html/front-end/searcher/searcher.js
@@ -357,7 +357,7 @@ window.search = window.search || {};
e.shiftKey ||
e.target.type === 'textarea' ||
e.target.type === 'text' ||
- !hasFocus() && /^(?:input|select|textarea)$/i.test(e.target.nodeName)
+ !hasFocus() && mdbook_something_else_has_focus(e)
) {
return;
}
diff --git a/tests/gui/books/editor/book.toml b/tests/gui/books/editor/book.toml
new file mode 100644
index 00000000..2af92d4c
--- /dev/null
+++ b/tests/gui/books/editor/book.toml
@@ -0,0 +1,6 @@
+[book]
+title = "editor"
+language = "en"
+
+[output.html.playground]
+editable = true
diff --git a/tests/gui/books/editor/src/SUMMARY.md b/tests/gui/books/editor/src/SUMMARY.md
new file mode 100644
index 00000000..fa67cf3e
--- /dev/null
+++ b/tests/gui/books/editor/src/SUMMARY.md
@@ -0,0 +1,4 @@
+# Summary
+
+- [Chapter 1](./chapter_1.md)
+- [Chapter 2](./chapter_2.md)
diff --git a/tests/gui/books/editor/src/chapter_1.md b/tests/gui/books/editor/src/chapter_1.md
new file mode 100644
index 00000000..8982f7d3
--- /dev/null
+++ b/tests/gui/books/editor/src/chapter_1.md
@@ -0,0 +1,7 @@
+# Chapter 1
+
+```rust,editable
+fn main() {
+ println!("Hello, world!");
+}
+```
diff --git a/tests/gui/books/editor/src/chapter_2.md b/tests/gui/books/editor/src/chapter_2.md
new file mode 100644
index 00000000..7ebb5961
--- /dev/null
+++ b/tests/gui/books/editor/src/chapter_2.md
@@ -0,0 +1 @@
+# Chapter 2
diff --git a/tests/gui/books/shadow-dom/book.toml b/tests/gui/books/shadow-dom/book.toml
new file mode 100644
index 00000000..edba3344
--- /dev/null
+++ b/tests/gui/books/shadow-dom/book.toml
@@ -0,0 +1,6 @@
+[book]
+title = "shadow-dom"
+language = "en"
+
+[output.html]
+additional-js = ["shadow-dom.js"]
diff --git a/tests/gui/books/shadow-dom/shadow-dom.js b/tests/gui/books/shadow-dom/shadow-dom.js
new file mode 100644
index 00000000..950442e2
--- /dev/null
+++ b/tests/gui/books/shadow-dom/shadow-dom.js
@@ -0,0 +1,38 @@
+(function() {
+ let shadowHost = null;
+ let shadowInput = null;
+
+ document.addEventListener('keypress', function(e) {
+ if (e.key === 'x' || e.key === 'X') {
+ if (shadowHost && shadowHost.isConnected) {
+ shadowInput.focus();
+ return;
+ }
+
+ shadowHost = document.createElement('div');
+ shadowHost.id = 'shadow-input-host';
+ shadowHost.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:9999;';
+
+ document.body.appendChild(shadowHost);
+
+ const shadowRoot = shadowHost.attachShadow({ mode: 'open' });
+
+ shadowInput = document.createElement('input');
+ shadowInput.type = 'text';
+ shadowInput.id = 'shadow-input';
+ shadowInput.placeholder = 'Shadow DOM input (press Escape to close)';
+ shadowInput.style.cssText = 'font-size:1.2em;padding:8px;width:300px;';
+
+ shadowRoot.appendChild(shadowInput);
+ shadowInput.focus();
+
+ shadowInput.addEventListener('keydown', function(e) {
+ if (e.key === 'Escape') {
+ shadowHost.remove();
+ shadowHost = null;
+ shadowInput = null;
+ }
+ });
+ }
+ });
+})();
diff --git a/tests/gui/books/shadow-dom/src/SUMMARY.md b/tests/gui/books/shadow-dom/src/SUMMARY.md
new file mode 100644
index 00000000..7390c828
--- /dev/null
+++ b/tests/gui/books/shadow-dom/src/SUMMARY.md
@@ -0,0 +1,3 @@
+# Summary
+
+- [Chapter 1](./chapter_1.md)
diff --git a/tests/gui/books/shadow-dom/src/chapter_1.md b/tests/gui/books/shadow-dom/src/chapter_1.md
new file mode 100644
index 00000000..b743fda3
--- /dev/null
+++ b/tests/gui/books/shadow-dom/src/chapter_1.md
@@ -0,0 +1 @@
+# Chapter 1
diff --git a/tests/gui/editor-keypress.goml b/tests/gui/editor-keypress.goml
new file mode 100644
index 00000000..f2cfe6f2
--- /dev/null
+++ b/tests/gui/editor-keypress.goml
@@ -0,0 +1,24 @@
+// Tests for global keypress handlers when ACE editor is in focus.
+// See https://github.com/rust-lang/mdBook/issues/3064
+
+go-to: |DOC_PATH| + "editor/chapter_1.html"
+
+click: ".ace_editor"
+press-key: "s"
+// Wait briefly to allow any event handlers triggered by the keypress to run.
+// Otherwise there is a race here since the wrapper is already display:none.
+wait-for: 200
+wait-for-css: ("#mdbook-search-wrapper", {"display": "none"})
+
+// ? inside ACE editor shouldn't show global help popup.
+press-key: "?"
+wait-for: 200
+wait-for-css: ("#mdbook-help-container", {"display": "none"})
+
+// Make sure arrow keys don"t navigate.
+press-key: "ArrowRight"
+wait-for: 200
+assert-window-property: ({"location": |DOC_PATH| + "editor/chapter_1.html"})
+press-key: "ArrowLeft"
+wait-for: 200
+assert-window-property: ({"location": |DOC_PATH| + "editor/chapter_1.html"})
diff --git a/tests/gui/shadow-dom.goml b/tests/gui/shadow-dom.goml
new file mode 100644
index 00000000..2b401f20
--- /dev/null
+++ b/tests/gui/shadow-dom.goml
@@ -0,0 +1,20 @@
+// Tests the keypress handler when there is a shadow-dom element.
+// See https://github.com/rust-lang/mdBook/issues/2507
+
+go-to: |DOC_PATH| + "shadow-dom/chapter_1.html"
+
+// Open the shadow-dom generated element.
+press-key: 'x'
+wait-for: '#shadow-input-host'
+// See what happens when the s key is pressed.
+press-key: 's'
+wait-for: 200
+wait-for-css: ("#mdbook-search-wrapper", {"display": "none"})
+
+// Also try the global key handlers.
+reload:
+press-key: 'x'
+wait-for: '#shadow-input-host'
+press-key: '?'
+wait-for: 200
+wait-for-css: ("#mdbook-help-container", {"display": "none"})