blob: 25e03d1b8878990cdd15f344cef0f8ca8889f68c [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Dirk Mueller (mueller@kde.org)
* Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
* (C) 2006 Alexey Proskuryakov (ap@nypop.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "third_party/blink/renderer/core/html/forms/html_form_control_element.h"
#include "third_party/blink/renderer/bindings/core/v8/usv_string_or_trusted_url.h"
#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
#include "third_party/blink/renderer/core/dom/element_traversal.h"
#include "third_party/blink/renderer/core/dom/events/event.h"
#include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
#include "third_party/blink/renderer/core/frame/use_counter.h"
#include "third_party/blink/renderer/core/html/forms/html_form_element.h"
#include "third_party/blink/renderer/core/html/forms/validity_state.h"
#include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
using namespace html_names;
HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tag_name,
Document& document)
: HTMLElement(tag_name, document),
autofill_state_(WebAutofillState::kNotFilled),
blocks_form_submission_(false) {
SetHasCustomStyleCallbacks();
static unsigned next_free_unique_id = 0;
unique_renderer_form_control_id_ = next_free_unique_id++;
}
HTMLFormControlElement::~HTMLFormControlElement() = default;
void HTMLFormControlElement::Trace(Visitor* visitor) {
ListedElement::Trace(visitor);
HTMLElement::Trace(visitor);
}
void HTMLFormControlElement::formAction(USVStringOrTrustedURL& result) const {
const AtomicString& action = FastGetAttribute(kFormactionAttr);
if (action.IsEmpty()) {
result.SetUSVString(GetDocument().Url());
return;
}
result.SetUSVString(
GetDocument().CompleteURL(StripLeadingAndTrailingHTMLSpaces(action)));
}
void HTMLFormControlElement::setFormAction(const USVStringOrTrustedURL& value,
ExceptionState& exception_state) {
setAttribute(kFormactionAttr, value, exception_state);
}
String HTMLFormControlElement::formEnctype() const {
const AtomicString& form_enctype_attr = FastGetAttribute(kFormenctypeAttr);
if (form_enctype_attr.IsNull())
return g_empty_string;
return FormSubmission::Attributes::ParseEncodingType(form_enctype_attr);
}
void HTMLFormControlElement::setFormEnctype(const AtomicString& value) {
setAttribute(kFormenctypeAttr, value);
}
String HTMLFormControlElement::formMethod() const {
const AtomicString& form_method_attr = FastGetAttribute(kFormmethodAttr);
if (form_method_attr.IsNull())
return g_empty_string;
return FormSubmission::Attributes::MethodString(
FormSubmission::Attributes::ParseMethodType(form_method_attr));
}
void HTMLFormControlElement::setFormMethod(const AtomicString& value) {
setAttribute(kFormmethodAttr, value);
}
bool HTMLFormControlElement::FormNoValidate() const {
return FastHasAttribute(kFormnovalidateAttr);
}
void HTMLFormControlElement::Reset() {
SetAutofillState(WebAutofillState::kNotFilled);
ResetImpl();
}
void HTMLFormControlElement::AttributeChanged(
const AttributeModificationParams& params) {
HTMLElement::AttributeChanged(params);
if (params.name == kDisabledAttr &&
params.old_value.IsNull() != params.new_value.IsNull()) {
DisabledAttributeChanged();
if (params.reason == AttributeModificationReason::kDirectly &&
IsDisabledFormControl() && AdjustedFocusedElementInTreeScope() == this)
blur();
}
}
void HTMLFormControlElement::ParseAttribute(
const AttributeModificationParams& params) {
const QualifiedName& name = params.name;
if (name == kFormAttr) {
FormAttributeChanged();
UseCounter::Count(GetDocument(), WebFeature::kFormAttribute);
} else if (name == kReadonlyAttr) {
if (params.old_value.IsNull() != params.new_value.IsNull()) {
UpdateWillValidateCache();
PseudoStateChanged(CSSSelector::kPseudoReadOnly);
PseudoStateChanged(CSSSelector::kPseudoReadWrite);
if (LayoutObject* o = GetLayoutObject())
o->InvalidateIfControlStateChanged(kReadOnlyControlState);
}
} else if (name == kRequiredAttr) {
if (params.old_value.IsNull() != params.new_value.IsNull())
RequiredAttributeChanged();
UseCounter::Count(GetDocument(), WebFeature::kRequiredAttribute);
} else if (name == kAutofocusAttr) {
HTMLElement::ParseAttribute(params);
UseCounter::Count(GetDocument(), WebFeature::kAutoFocusAttribute);
} else {
HTMLElement::ParseAttribute(params);
}
}
void HTMLFormControlElement::DisabledAttributeChanged() {
// Don't blur in this function because this is called for descendants of
// <fieldset> while tree traversal.
EventDispatchForbiddenScope event_forbidden;
ListedElement::DisabledAttributeChanged();
if (LayoutObject* o = GetLayoutObject())
o->InvalidateIfControlStateChanged(kEnabledControlState);
// TODO(dmazzoni): http://crbug.com/699438.
// Replace |CheckedStateChanged| with a generic tree changed event.
if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache())
cache->CheckedStateChanged(this);
}
void HTMLFormControlElement::RequiredAttributeChanged() {
SetNeedsValidityCheck();
PseudoStateChanged(CSSSelector::kPseudoRequired);
PseudoStateChanged(CSSSelector::kPseudoOptional);
// TODO(dmazzoni): http://crbug.com/699438.
// Replace |CheckedStateChanged| with a generic tree changed event.
if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache())
cache->CheckedStateChanged(this);
}
bool HTMLFormControlElement::IsReadOnly() const {
return FastHasAttribute(html_names::kReadonlyAttr);
}
bool HTMLFormControlElement::IsDisabledOrReadOnly() const {
return IsDisabledFormControl() || IsReadOnly();
}
bool HTMLFormControlElement::SupportsAutofocus() const {
return false;
}
bool HTMLFormControlElement::IsAutofocusable() const {
return FastHasAttribute(kAutofocusAttr) && SupportsAutofocus();
}
void HTMLFormControlElement::SetAutofillState(WebAutofillState autofill_state) {
if (autofill_state == autofill_state_)
return;
autofill_state_ = autofill_state;
PseudoStateChanged(CSSSelector::kPseudoAutofill);
PseudoStateChanged(CSSSelector::kPseudoAutofillSelected);
PseudoStateChanged(CSSSelector::kPseudoAutofillPreviewed);
}
void HTMLFormControlElement::SetAutofillSection(const WebString& section) {
autofill_section_ = section;
}
const AtomicString& HTMLFormControlElement::autocapitalize() const {
if (!FastGetAttribute(kAutocapitalizeAttr).IsEmpty())
return HTMLElement::autocapitalize();
// If the form control itself does not have the autocapitalize attribute set,
// but the form owner is non-null and does have the autocapitalize attribute
// set, we inherit from the form owner.
if (HTMLFormElement* form = Form())
return form->autocapitalize();
return g_empty_atom;
}
static bool ShouldAutofocusOnAttach(const HTMLFormControlElement* element) {
if (!element->IsAutofocusable())
return false;
if (element->GetDocument().IsSandboxed(kSandboxAutomaticFeatures)) {
// FIXME: This message should be moved off the console once a solution to
// https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
element->GetDocument().AddConsoleMessage(ConsoleMessage::Create(
kSecurityMessageSource, kErrorMessageLevel,
"Blocked autofocusing on a form control because the form's frame is "
"sandboxed and the 'allow-scripts' permission is not set."));
return false;
}
return true;
}
void HTMLFormControlElement::AttachLayoutTree(AttachContext& context) {
HTMLElement::AttachLayoutTree(context);
if (!GetLayoutObject())
return;
// The call to updateFromElement() needs to go after the call through
// to the base class's attachLayoutTree() because that can sometimes do a
// close on the layoutObject.
GetLayoutObject()->UpdateFromElement();
// FIXME: Autofocus handling should be moved to insertedInto according to
// the standard.
if (ShouldAutofocusOnAttach(this))
GetDocument().SetAutofocusElement(this);
}
void HTMLFormControlElement::DidMoveToNewDocument(Document& old_document) {
ListedElement::DidMoveToNewDocument(old_document);
HTMLElement::DidMoveToNewDocument(old_document);
}
Node::InsertionNotificationRequest HTMLFormControlElement::InsertedInto(
ContainerNode& insertion_point) {
HTMLElement::InsertedInto(insertion_point);
ListedElement::InsertedInto(insertion_point);
return kInsertionDone;
}
void HTMLFormControlElement::RemovedFrom(ContainerNode& insertion_point) {
HTMLElement::RemovedFrom(insertion_point);
ListedElement::RemovedFrom(insertion_point);
}
void HTMLFormControlElement::WillChangeForm() {
ListedElement::WillChangeForm();
if (formOwner() && CanBeSuccessfulSubmitButton())
formOwner()->InvalidateDefaultButtonStyle();
}
void HTMLFormControlElement::DidChangeForm() {
ListedElement::DidChangeForm();
if (formOwner() && isConnected() && CanBeSuccessfulSubmitButton())
formOwner()->InvalidateDefaultButtonStyle();
}
void HTMLFormControlElement::DispatchChangeEvent() {
DispatchScopedEvent(*Event::CreateBubble(event_type_names::kChange));
}
HTMLFormElement* HTMLFormControlElement::formOwner() const {
return ListedElement::Form();
}
bool HTMLFormControlElement::IsDisabledFormControl() const {
// Since the MHTML is loaded in sandboxing mode with form submission and
// script execution disabled, we should gray out all form control elements
// to indicate that the form cannot be worked on.
if (GetDocument().Fetcher()->Archive())
return true;
return IsActuallyDisabled();
}
bool HTMLFormControlElement::MatchesEnabledPseudoClass() const {
return !IsDisabledFormControl();
}
bool HTMLFormControlElement::IsRequired() const {
return FastHasAttribute(kRequiredAttr);
}
String HTMLFormControlElement::ResultForDialogSubmit() {
return FastGetAttribute(kValueAttr);
}
void HTMLFormControlElement::DidRecalcStyle(StyleRecalcChange) {
if (LayoutObject* layout_object = GetLayoutObject())
layout_object->UpdateFromElement();
}
bool HTMLFormControlElement::SupportsFocus() const {
return !IsDisabledFormControl();
}
bool HTMLFormControlElement::IsKeyboardFocusable() const {
// Skip tabIndex check in a parent class.
return IsFocusable();
}
bool HTMLFormControlElement::MayTriggerVirtualKeyboard() const {
return false;
}
bool HTMLFormControlElement::ShouldHaveFocusAppearance() const {
return (GetDocument().LastFocusType() != kWebFocusTypeMouse) ||
GetDocument().HadKeyboardEvent() || MayTriggerVirtualKeyboard();
}
int HTMLFormControlElement::tabIndex() const {
// Skip the supportsFocus check in HTMLElement.
return Element::tabIndex();
}
bool HTMLFormControlElement::willValidate() const {
return ListedElement::WillValidate();
}
bool HTMLFormControlElement::MatchesValidityPseudoClasses() const {
return willValidate();
}
bool HTMLFormControlElement::IsValidElement() {
return ListedElement::IsValidElement();
}
void HTMLFormControlElement::DispatchBlurEvent(
Element* new_focused_element,
WebFocusType type,
InputDeviceCapabilities* source_capabilities) {
HTMLElement::DispatchBlurEvent(new_focused_element, type,
source_capabilities);
HideVisibleValidationMessage();
}
bool HTMLFormControlElement::IsSuccessfulSubmitButton() const {
return CanBeSuccessfulSubmitButton() && !IsDisabledFormControl();
}
// static
const HTMLFormControlElement*
HTMLFormControlElement::EnclosingFormControlElement(const Node* node) {
if (!node)
return nullptr;
return Traversal<HTMLFormControlElement>::FirstAncestorOrSelf(*node);
}
String HTMLFormControlElement::NameForAutofill() const {
String full_name = GetName();
String trimmed_name = full_name.StripWhiteSpace();
if (!trimmed_name.IsEmpty())
return trimmed_name;
full_name = GetIdAttribute();
trimmed_name = full_name.StripWhiteSpace();
return trimmed_name;
}
void HTMLFormControlElement::CloneNonAttributePropertiesFrom(
const Element& source,
CloneChildrenFlag flag) {
HTMLElement::CloneNonAttributePropertiesFrom(source, flag);
SetNeedsValidityCheck();
}
void HTMLFormControlElement::AssociateWith(HTMLFormElement* form) {
AssociateByParser(form);
};
} // namespace blink