/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::borrow::Cow;
use std::cell::Cell;
use std::cmp::Ordering;
use std::ops::Range;
use std::path::PathBuf;
use std::ptr::NonNull;
use std::str::FromStr;
use std::{f64, ptr};
use dom_struct::dom_struct;
use embedder_traits::{
EmbedderMsg, FilterPattern, FormControl as EmbedderFormControl, InputMethodType, RgbColor,
};
use encoding_rs::Encoding;
use euclid::{Point2D, Rect, Size2D};
use html5ever::{LocalName, Prefix, local_name, ns};
use ipc_channel::ipc;
use js::jsapi::{
ClippedTime, DateGetMsecSinceEpoch, Handle, JS_ClearPendingException, JSObject, NewDateObject,
NewUCRegExpObject, ObjectIsDate, RegExpFlag_UnicodeSets, RegExpFlags,
};
use js::jsval::UndefinedValue;
use js::rust::wrappers::{CheckRegExpSyntax, ExecuteRegExpNoStatics, ObjectIsRegExp};
use js::rust::{HandleObject, MutableHandleObject};
use net_traits::blob_url_store::get_blob_origin;
use net_traits::filemanager_thread::FileManagerThreadMsg;
use net_traits::{CoreResourceMsg, IpcSend};
use script_bindings::codegen::GenericBindings::ShadowRootBinding::{
ShadowRootMode, SlotAssignmentMode,
};
use style::attr::AttrValue;
use style::str::{split_commas, str_join};
use stylo_atoms::Atom;
use stylo_dom::ElementState;
use time::{Month, OffsetDateTime, Time};
use unicode_bidi::{BidiClass, bidi_class};
use url::Url;
use webrender_api::units::DeviceIntRect;
use crate::clipboard_provider::EmbedderClipboardProvider;
use crate::dom::activation::Activatable;
use crate::dom::attr::Attr;
use crate::dom::bindings::cell::{DomRefCell, Ref};
use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use crate::dom::bindings::codegen::Bindings::FileListBinding::FileListMethods;
use crate::dom::bindings::codegen::Bindings::HTMLFormElementBinding::SelectionMode;
use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
use crate::dom::bindings::error::{Error, ErrorResult};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::DomGlobal;
use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
use crate::dom::bindings::str::{DOMString, FromInputValueString, ToInputValueString, USVString};
use crate::dom::clipboardevent::ClipboardEvent;
use crate::dom::compositionevent::CompositionEvent;
use crate::dom::document::Document;
use crate::dom::element::{AttributeMutation, Element, ElementCreator, LayoutElementHelpers};
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::eventtarget::EventTarget;
use crate::dom::file::File;
use crate::dom::filelist::{FileList, LayoutFileListHelpers};
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmldatalistelement::HTMLDataListElement;
use crate::dom::htmldivelement::HTMLDivElement;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlfieldsetelement::HTMLFieldSetElement;
use crate::dom::htmlformelement::{
FormControl, FormDatum, FormDatumValue, FormSubmitterElement, HTMLFormElement, ResetFrom,
SubmittedFrom,
};
use crate::dom::htmlstyleelement::HTMLStyleElement;
use crate::dom::keyboardevent::KeyboardEvent;
use crate::dom::mouseevent::MouseEvent;
use crate::dom::node::{
BindContext, CloneChildrenFlag, Node, NodeDamage, NodeTraits, ShadowIncluding, UnbindContext,
};
use crate::dom::nodelist::NodeList;
use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
use crate::dom::validitystate::{ValidationFlags, ValidityState};
use crate::dom::virtualmethods::VirtualMethods;
use crate::realms::enter_realm;
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
use crate::textinput::KeyReaction::{
DispatchInput, Nothing, RedrawSelection, TriggerDefaultAction,
};
use crate::textinput::Lines::Single;
use crate::textinput::{
Direction, SelectionDirection, TextInput, UTF8Bytes, UTF16CodeUnits,
handle_text_clipboard_action,
};
const DEFAULT_SUBMIT_VALUE: &str = "Submit";
const DEFAULT_RESET_VALUE: &str = "Reset";
const PASSWORD_REPLACEMENT_CHAR: char = '●';
const DEFAULT_FILE_INPUT_VALUE: &str = "No file chosen";
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
/// Contains references to the elements in the shadow tree for ``.
///
/// The shadow tree consists of a single div with the currently selected color as
/// the background.
struct InputTypeColorShadowTree {
color_value: Dom,
}
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
#[non_exhaustive]
enum ShadowTree {
Color(InputTypeColorShadowTree),
// TODO: Add shadow trees for other input types (range etc) here
}
const COLOR_TREE_STYLE: &str = "
#color-value {
width: 100%;
height: 100%;
box-sizing: border-box;
border: 1px solid gray;
border-radius: 2px;
}
";
///
#[derive(Clone, Copy, Default, JSTraceable, PartialEq)]
#[allow(dead_code)]
#[derive(MallocSizeOf)]
pub(crate) enum InputType {
///
Button,
///
Checkbox,
///
Color,
///
Date,
///
DatetimeLocal,
///
Email,
///
File,
///
Hidden,
///
Image,
///
Month,
///
Number,
///
Password,
///
Radio,
///
Range,
///
Reset,
///
Search,
///
Submit,
///
Tel,
///
#[default]
Text,
///
Time,
///
Url,
///
Week,
}
impl InputType {
// Note that Password is not included here since it is handled
// slightly differently, with placeholder characters shown rather
// than the underlying value.
fn is_textual(&self) -> bool {
matches!(
*self,
InputType::Date |
InputType::DatetimeLocal |
InputType::Email |
InputType::Hidden |
InputType::Month |
InputType::Number |
InputType::Range |
InputType::Search |
InputType::Tel |
InputType::Text |
InputType::Time |
InputType::Url |
InputType::Week
)
}
fn is_textual_or_password(&self) -> bool {
self.is_textual() || *self == InputType::Password
}
// https://html.spec.whatwg.org/multipage/#has-a-periodic-domain
fn has_periodic_domain(&self) -> bool {
*self == InputType::Time
}
fn as_str(&self) -> &str {
match *self {
InputType::Button => "button",
InputType::Checkbox => "checkbox",
InputType::Color => "color",
InputType::Date => "date",
InputType::DatetimeLocal => "datetime-local",
InputType::Email => "email",
InputType::File => "file",
InputType::Hidden => "hidden",
InputType::Image => "image",
InputType::Month => "month",
InputType::Number => "number",
InputType::Password => "password",
InputType::Radio => "radio",
InputType::Range => "range",
InputType::Reset => "reset",
InputType::Search => "search",
InputType::Submit => "submit",
InputType::Tel => "tel",
InputType::Text => "text",
InputType::Time => "time",
InputType::Url => "url",
InputType::Week => "week",
}
}
pub(crate) fn as_ime_type(&self) -> Option {
match *self {
InputType::Color => Some(InputMethodType::Color),
InputType::Date => Some(InputMethodType::Date),
InputType::DatetimeLocal => Some(InputMethodType::DatetimeLocal),
InputType::Email => Some(InputMethodType::Email),
InputType::Month => Some(InputMethodType::Month),
InputType::Number => Some(InputMethodType::Number),
InputType::Password => Some(InputMethodType::Password),
InputType::Search => Some(InputMethodType::Search),
InputType::Tel => Some(InputMethodType::Tel),
InputType::Text => Some(InputMethodType::Text),
InputType::Time => Some(InputMethodType::Time),
InputType::Url => Some(InputMethodType::Url),
InputType::Week => Some(InputMethodType::Week),
_ => None,
}
}
}
impl From<&Atom> for InputType {
fn from(value: &Atom) -> InputType {
match value.to_ascii_lowercase() {
atom!("button") => InputType::Button,
atom!("checkbox") => InputType::Checkbox,
atom!("color") => InputType::Color,
atom!("date") => InputType::Date,
atom!("datetime-local") => InputType::DatetimeLocal,
atom!("email") => InputType::Email,
atom!("file") => InputType::File,
atom!("hidden") => InputType::Hidden,
atom!("image") => InputType::Image,
atom!("month") => InputType::Month,
atom!("number") => InputType::Number,
atom!("password") => InputType::Password,
atom!("radio") => InputType::Radio,
atom!("range") => InputType::Range,
atom!("reset") => InputType::Reset,
atom!("search") => InputType::Search,
atom!("submit") => InputType::Submit,
atom!("tel") => InputType::Tel,
atom!("text") => InputType::Text,
atom!("time") => InputType::Time,
atom!("url") => InputType::Url,
atom!("week") => InputType::Week,
_ => Self::default(),
}
}
}
#[derive(Debug, PartialEq)]
enum ValueMode {
///
Value,
///
Default,
///
DefaultOn,
///
Filename,
}
#[derive(Debug, PartialEq)]
enum StepDirection {
Up,
Down,
}
#[dom_struct]
pub(crate) struct HTMLInputElement {
htmlelement: HTMLElement,
input_type: Cell,
///
checked_changed: Cell,
placeholder: DomRefCell,
size: Cell,
maxlength: Cell,
minlength: Cell,
#[ignore_malloc_size_of = "TextInput contains an IPCSender which cannot be measured"]
#[no_trace]
textinput: DomRefCell>,
// https://html.spec.whatwg.org/multipage/#concept-input-value-dirty-flag
value_dirty: Cell,
// not specified explicitly, but implied by the fact that sanitization can't
// happen until after all of step/min/max/value content attributes have
// been added
sanitization_flag: Cell,
filelist: MutNullableDom,
form_owner: MutNullableDom,
labels_node_list: MutNullableDom,
validity_state: MutNullableDom,
shadow_tree: DomRefCell