script/dom/execcommand/
execcommands.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
6use crate::dom::bindings::root::DomRoot;
7use crate::dom::bindings::str::DOMString;
8use crate::dom::document::Document;
9use crate::dom::execcommand::basecommand::BaseCommand;
10use crate::dom::execcommand::commands::delete::DeleteCommand;
11use crate::dom::selection::Selection;
12use crate::script_runtime::CanGc;
13
14impl Document {
15    /// <https://w3c.github.io/editing/docs/execCommand/#enabled>
16    fn selection_if_command_is_enabled(&self, can_gc: CanGc) -> Option<DomRoot<Selection>> {
17        // > Among commands defined in this specification, those listed in Miscellaneous commands are always enabled,
18        // > except for the cut command and the paste command.
19        // TODO
20        // > The other commands defined here are enabled if the active range is not null,
21        let selection = self.GetSelection(can_gc)?;
22        let range = selection.active_range()?;
23        // > its start node is either editable or an editing host,
24        let start = range.start_container();
25        if !start.is_editable() && !start.is_editing_host() {
26            return None;
27        }
28        // > the editing host of its start node is not an EditContext editing host,
29        // TODO
30        // > its end node is either editable or an editing host,
31        let end = range.end_container();
32        if !end.is_editable() && !end.is_editing_host() {
33            return None;
34        }
35        // > the editing host of its end node is not an EditContext editing host,
36        // TODO
37        // > and there is some editing host that is an inclusive ancestor of both its start node and its end node.
38        // TODO
39        Some(selection)
40    }
41
42    /// <https://w3c.github.io/editing/docs/execCommand/#supported>
43    fn command_if_command_is_supported(
44        &self,
45        command_id: DOMString,
46    ) -> Option<Box<dyn BaseCommand>> {
47        Some(Box::new(match &*command_id.str() {
48            "delete" => DeleteCommand {},
49            _ => return None,
50        }))
51    }
52}
53
54pub(crate) trait ExecCommandsSupport {
55    fn check_support_and_enabled(
56        &self,
57        command_id: DOMString,
58        can_gc: CanGc,
59    ) -> Option<(Box<dyn BaseCommand>, DomRoot<Selection>)>;
60    fn exec_command_for_command_id(
61        &self,
62        command_id: DOMString,
63        value: DOMString,
64        can_gc: CanGc,
65    ) -> bool;
66}
67
68impl ExecCommandsSupport for Document {
69    /// <https://w3c.github.io/editing/docs/execCommand/#querycommandenabled()>
70    fn check_support_and_enabled(
71        &self,
72        command_id: DOMString,
73        can_gc: CanGc,
74    ) -> Option<(Box<dyn BaseCommand>, DomRoot<Selection>)> {
75        // Step 2. Return true if command is both supported and enabled, false otherwise.
76        self.command_if_command_is_supported(command_id)
77            .zip(self.selection_if_command_is_enabled(can_gc))
78    }
79
80    /// <https://w3c.github.io/editing/docs/execCommand/#execcommand()>
81    fn exec_command_for_command_id(
82        &self,
83        command_id: DOMString,
84        value: DOMString,
85        can_gc: CanGc,
86    ) -> bool {
87        // Step 3. If command is not supported or not enabled, return false.
88        let Some((command, selection)) = self.check_support_and_enabled(command_id, can_gc) else {
89            return false;
90        };
91        // Step 4. If command is not in the Miscellaneous commands section:
92        // TODO
93
94        // Step 4.1. Let affected editing host be the editing host that is an inclusive ancestor
95        // of the active range's start node and end node, and is not the ancestor of any editing host
96        // that is an inclusive ancestor of the active range's start node and end node.
97        // TODO
98
99        // Step 4.2. Fire an event named "beforeinput" at affected editing host using InputEvent,
100        // with its bubbles and cancelable attributes initialized to true, and its data attribute initialized to null
101        // TODO
102
103        // Step 4.3. If the value returned by the previous step is false, return false.
104        // TODO
105
106        // Step 4.4. If command is not enabled, return false.
107        // TODO
108
109        // Step 4.5. Let affected editing host be the editing host that is an inclusive ancestor
110        // of the active range's start node and end node, and is not the ancestor of any editing host
111        // that is an inclusive ancestor of the active range's start node and end node.
112        // TODO
113
114        // Step 5. Take the action for command, passing value to the instructions as an argument.
115        let result = command.execute(&selection, value);
116        // Step 6. If the previous step returned false, return false.
117        if !result {
118            return false;
119        }
120        // Step 7. If the action modified DOM tree, then fire an event named "input" at affected editing
121        // host using InputEvent, with its isTrusted and bubbles attributes initialized to true,
122        // inputType attribute initialized to the mapped value of command, and its data attribute initialized to null.
123        // TODO
124
125        // Step 8. Return true.
126        true
127    }
128}