123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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 http://mozilla.org/MPL/2.0/. */
- #include "mozilla/dom/HTMLButtonElement.h"
- #include "HTMLFormSubmissionConstants.h"
- #include "mozilla/dom/HTMLButtonElementBinding.h"
- #include "mozilla/dom/HTMLFormSubmission.h"
- #include "nsIDOMHTMLFormElement.h"
- #include "nsAttrValueInlines.h"
- #include "nsGkAtoms.h"
- #include "nsIPresShell.h"
- #include "nsStyleConsts.h"
- #include "nsPresContext.h"
- #include "nsIFormControl.h"
- #include "nsIURL.h"
- #include "nsIFrame.h"
- #include "nsIFormControlFrame.h"
- #include "nsIDOMEvent.h"
- #include "nsIDocument.h"
- #include "mozilla/ContentEvents.h"
- #include "mozilla/EventDispatcher.h"
- #include "mozilla/EventStateManager.h"
- #include "mozilla/EventStates.h"
- #include "mozilla/MouseEvents.h"
- #include "mozilla/TextEvents.h"
- #include "nsUnicharUtils.h"
- #include "nsLayoutUtils.h"
- #include "nsPresState.h"
- #include "nsError.h"
- #include "nsFocusManager.h"
- #include "mozilla/dom/HTMLFormElement.h"
- #include "mozAutoDocUpdate.h"
- #define NS_IN_SUBMIT_CLICK (1 << 0)
- #define NS_OUTER_ACTIVATE_EVENT (1 << 1)
- NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Button)
- namespace mozilla {
- namespace dom {
- static const nsAttrValue::EnumTable kButtonTypeTable[] = {
- { "button", NS_FORM_BUTTON_BUTTON },
- { "reset", NS_FORM_BUTTON_RESET },
- { "submit", NS_FORM_BUTTON_SUBMIT },
- { nullptr, 0 }
- };
- // Default type is 'submit'.
- static const nsAttrValue::EnumTable* kButtonDefaultType = &kButtonTypeTable[2];
- // Construction, destruction
- HTMLButtonElement::HTMLButtonElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
- FromParser aFromParser)
- : nsGenericHTMLFormElementWithState(aNodeInfo),
- mType(kButtonDefaultType->value),
- mDisabledChanged(false),
- mInInternalActivate(false),
- mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT))
- {
- // Set up our default state: enabled
- AddStatesSilently(NS_EVENT_STATE_ENABLED);
- }
- HTMLButtonElement::~HTMLButtonElement()
- {
- }
- // nsISupports
- NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLButtonElement,
- nsGenericHTMLFormElementWithState,
- mValidity)
- NS_IMPL_ADDREF_INHERITED(HTMLButtonElement, Element)
- NS_IMPL_RELEASE_INHERITED(HTMLButtonElement, Element)
- // QueryInterface implementation for HTMLButtonElement
- NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLButtonElement)
- NS_INTERFACE_TABLE_INHERITED(HTMLButtonElement,
- nsIDOMHTMLButtonElement,
- nsIConstraintValidation)
- NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLFormElementWithState)
- // nsIConstraintValidation
- NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_SETCUSTOMVALIDITY(HTMLButtonElement)
- NS_IMETHODIMP
- HTMLButtonElement::SetCustomValidity(const nsAString& aError)
- {
- nsIConstraintValidation::SetCustomValidity(aError);
-
- UpdateState(true);
- return NS_OK;
- }
- void
- HTMLButtonElement::UpdateBarredFromConstraintValidation()
- {
- SetBarredFromConstraintValidation(mType == NS_FORM_BUTTON_BUTTON ||
- mType == NS_FORM_BUTTON_RESET ||
- IsDisabled());
- }
- void
- HTMLButtonElement::FieldSetDisabledChanged(bool aNotify)
- {
- UpdateBarredFromConstraintValidation();
- nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
- }
- // nsIDOMHTMLButtonElement
- NS_IMPL_ELEMENT_CLONE(HTMLButtonElement)
- // nsIDOMHTMLButtonElement
- NS_IMETHODIMP
- HTMLButtonElement::GetForm(nsIDOMHTMLFormElement** aForm)
- {
- return nsGenericHTMLFormElementWithState::GetForm(aForm);
- }
- NS_IMPL_BOOL_ATTR(HTMLButtonElement, Autofocus, autofocus)
- NS_IMPL_BOOL_ATTR(HTMLButtonElement, Disabled, disabled)
- NS_IMPL_ACTION_ATTR(HTMLButtonElement, FormAction, formaction)
- NS_IMPL_ENUM_ATTR_DEFAULT_MISSING_INVALID_VALUES(HTMLButtonElement, FormEnctype, formenctype,
- "", kFormDefaultEnctype->tag)
- NS_IMPL_ENUM_ATTR_DEFAULT_MISSING_INVALID_VALUES(HTMLButtonElement, FormMethod, formmethod,
- "", kFormDefaultMethod->tag)
- NS_IMPL_BOOL_ATTR(HTMLButtonElement, FormNoValidate, formnovalidate)
- NS_IMPL_STRING_ATTR(HTMLButtonElement, FormTarget, formtarget)
- NS_IMPL_STRING_ATTR(HTMLButtonElement, Name, name)
- NS_IMPL_STRING_ATTR(HTMLButtonElement, Value, value)
- NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLButtonElement, Type, type,
- kButtonDefaultType->tag)
- int32_t
- HTMLButtonElement::TabIndexDefault()
- {
- return 0;
- }
- bool
- HTMLButtonElement::IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex)
- {
- if (nsGenericHTMLFormElementWithState::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
- return true;
- }
- *aIsFocusable = !IsDisabled();
- return false;
- }
- bool
- HTMLButtonElement::ParseAttribute(int32_t aNamespaceID,
- nsIAtom* aAttribute,
- const nsAString& aValue,
- nsAttrValue& aResult)
- {
- if (aNamespaceID == kNameSpaceID_None) {
- if (aAttribute == nsGkAtoms::type) {
- // XXX ARG!! This is major evilness. ParseAttribute
- // shouldn't set members. Override SetAttr instead
- bool success = aResult.ParseEnumValue(aValue, kButtonTypeTable, false);
- if (success) {
- mType = aResult.GetEnumValue();
- } else {
- mType = kButtonDefaultType->value;
- }
- return success;
- }
- if (aAttribute == nsGkAtoms::formmethod) {
- return aResult.ParseEnumValue(aValue, kFormMethodTable, false);
- }
- if (aAttribute == nsGkAtoms::formenctype) {
- return aResult.ParseEnumValue(aValue, kFormEnctypeTable, false);
- }
- }
- return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
- aResult);
- }
- bool
- HTMLButtonElement::IsDisabledForEvents(WidgetEvent* aEvent)
- {
- nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
- nsIFrame* formFrame = do_QueryFrame(formControlFrame);
- return IsElementDisabledForEvents(aEvent, formFrame);
- }
- nsresult
- HTMLButtonElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
- {
- aVisitor.mCanHandle = false;
- if (IsDisabledForEvents(aVisitor.mEvent)) {
- return NS_OK;
- }
- // Track whether we're in the outermost Dispatch invocation that will
- // cause activation of the input. That is, if we're a click event, or a
- // DOMActivate that was dispatched directly, this will be set, but if we're
- // a DOMActivate dispatched from click handling, it will not be set.
- WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
- bool outerActivateEvent =
- ((mouseEvent && mouseEvent->IsLeftClickEvent()) ||
- (aVisitor.mEvent->mMessage == eLegacyDOMActivate &&
- !mInInternalActivate));
- if (outerActivateEvent) {
- aVisitor.mItemFlags |= NS_OUTER_ACTIVATE_EVENT;
- if (mType == NS_FORM_BUTTON_SUBMIT && mForm) {
- aVisitor.mItemFlags |= NS_IN_SUBMIT_CLICK;
- // tell the form that we are about to enter a click handler.
- // that means that if there are scripted submissions, the
- // latest one will be deferred until after the exit point of the handler.
- mForm->OnSubmitClickBegin(this);
- }
- }
- return nsGenericHTMLElement::GetEventTargetParent(aVisitor);
- }
- nsresult
- HTMLButtonElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
- {
- nsresult rv = NS_OK;
- if (!aVisitor.mPresContext) {
- return rv;
- }
- if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
- WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
- if (mouseEvent && mouseEvent->IsLeftClickEvent()) {
- // DOMActive event should be trusted since the activation is actually
- // occurred even if the cause is an untrusted click event.
- InternalUIEvent actEvent(true, eLegacyDOMActivate, mouseEvent);
- actEvent.mDetail = 1;
- nsCOMPtr<nsIPresShell> shell = aVisitor.mPresContext->GetPresShell();
- if (shell) {
- nsEventStatus status = nsEventStatus_eIgnore;
- mInInternalActivate = true;
- shell->HandleDOMEventWithTarget(this, &actEvent, &status);
- mInInternalActivate = false;
- // If activate is cancelled, we must do the same as when click is
- // cancelled (revert the checkbox to its original value).
- if (status == nsEventStatus_eConsumeNoDefault) {
- aVisitor.mEventStatus = status;
- }
- }
- }
- }
- // mForm is null if the event handler removed us from the document (bug 194582).
- if ((aVisitor.mItemFlags & NS_IN_SUBMIT_CLICK) && mForm) {
- // tell the form that we are about to exit a click handler
- // so the form knows not to defer subsequent submissions
- // the pending ones that were created during the handler
- // will be flushed or forgoten.
- mForm->OnSubmitClickEnd();
- }
- if (nsEventStatus_eIgnore == aVisitor.mEventStatus) {
- switch (aVisitor.mEvent->mMessage) {
- case eKeyPress:
- case eKeyUp:
- {
- // For backwards compat, trigger buttons with space or enter
- // (bug 25300)
- WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
- if ((keyEvent->mKeyCode == NS_VK_RETURN &&
- eKeyPress == aVisitor.mEvent->mMessage) ||
- (keyEvent->mKeyCode == NS_VK_SPACE &&
- eKeyUp == aVisitor.mEvent->mMessage)) {
- DispatchSimulatedClick(this, aVisitor.mEvent->IsTrusted(),
- aVisitor.mPresContext);
- aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
- }
- }
- break;
- default:
- break;
- }
- if (aVisitor.mItemFlags & NS_OUTER_ACTIVATE_EVENT) {
- if (mForm && (mType == NS_FORM_BUTTON_SUBMIT ||
- mType == NS_FORM_BUTTON_RESET)) {
- InternalFormEvent event(true,
- (mType == NS_FORM_BUTTON_RESET) ? eFormReset : eFormSubmit);
- event.mOriginator = this;
- nsEventStatus status = nsEventStatus_eIgnore;
- nsCOMPtr<nsIPresShell> presShell =
- aVisitor.mPresContext->GetPresShell();
- // If |nsIPresShell::Destroy| has been called due to
- // handling the event, the pres context will return
- // a null pres shell. See bug 125624.
- //
- // Using presShell to dispatch the event. It makes sure that
- // event is not handled if the window is being destroyed.
- if (presShell && (event.mMessage != eFormSubmit ||
- mForm->SubmissionCanProceed(this))) {
- // TODO: removing this code and have the submit event sent by the form
- // see bug 592124.
- // Hold a strong ref while dispatching
- RefPtr<HTMLFormElement> form(mForm);
- presShell->HandleDOMEventWithTarget(form, &event, &status);
- aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
- }
- }
- }
- } else if ((aVisitor.mItemFlags & NS_IN_SUBMIT_CLICK) && mForm) {
- // Tell the form to flush a possible pending submission.
- // the reason is that the script returned false (the event was
- // not ignored) so if there is a stored submission, it needs to
- // be submitted immediatelly.
- // Note, NS_IN_SUBMIT_CLICK is set only when we're in outer activate event.
- mForm->FlushPendingSubmission();
- } //if
- return rv;
- }
- nsresult
- HTMLButtonElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
- nsIContent* aBindingParent,
- bool aCompileEventHandlers)
- {
- nsresult rv =
- nsGenericHTMLFormElementWithState::BindToTree(aDocument, aParent, aBindingParent,
- aCompileEventHandlers);
- NS_ENSURE_SUCCESS(rv, rv);
- // Update our state; we may now be the default submit element
- UpdateState(false);
- return NS_OK;
- }
- void
- HTMLButtonElement::UnbindFromTree(bool aDeep, bool aNullParent)
- {
- nsGenericHTMLFormElementWithState::UnbindFromTree(aDeep, aNullParent);
- // Update our state; we may no longer be the default submit element
- UpdateState(false);
- }
- NS_IMETHODIMP
- HTMLButtonElement::Reset()
- {
- return NS_OK;
- }
- NS_IMETHODIMP
- HTMLButtonElement::SubmitNamesValues(HTMLFormSubmission* aFormSubmission)
- {
- //
- // We only submit if we were the button pressed
- //
- if (aFormSubmission->GetOriginatingElement() != this) {
- return NS_OK;
- }
- // Disabled elements don't submit
- if (IsDisabled()) {
- return NS_OK;
- }
- //
- // Get the name (if no name, no submit)
- //
- nsAutoString name;
- GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
- if (name.IsEmpty()) {
- return NS_OK;
- }
- //
- // Get the value
- //
- nsAutoString value;
- nsresult rv = GetValue(value);
- if (NS_FAILED(rv)) {
- return rv;
- }
- //
- // Submit
- //
- return aFormSubmission->AddNameValuePair(name, value);
- }
- void
- HTMLButtonElement::DoneCreatingElement()
- {
- if (!mInhibitStateRestoration) {
- nsresult rv = GenerateStateKey();
- if (NS_SUCCEEDED(rv)) {
- RestoreFormControlState();
- }
- }
- }
- nsresult
- HTMLButtonElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
- const nsAttrValueOrString* aValue,
- bool aNotify)
- {
- if (aNotify && aName == nsGkAtoms::disabled &&
- aNameSpaceID == kNameSpaceID_None) {
- mDisabledChanged = true;
- }
- return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName,
- aValue, aNotify);
- }
- nsresult
- HTMLButtonElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
- const nsAttrValue* aValue,
- const nsAttrValue* aOldValue, bool aNotify)
- {
- if (aNameSpaceID == kNameSpaceID_None) {
- if (aName == nsGkAtoms::type) {
- if (!aValue) {
- mType = kButtonDefaultType->value;
- }
- }
- if (aName == nsGkAtoms::type || aName == nsGkAtoms::disabled) {
- UpdateBarredFromConstraintValidation();
- }
- }
- return nsGenericHTMLFormElementWithState::AfterSetAttr(aNameSpaceID, aName,
- aValue, aOldValue,
- aNotify);
- }
- NS_IMETHODIMP
- HTMLButtonElement::SaveState()
- {
- if (!mDisabledChanged) {
- return NS_OK;
- }
-
- nsPresState* state = GetPrimaryPresState();
- if (state) {
- // We do not want to save the real disabled state but the disabled
- // attribute.
- state->SetDisabled(HasAttr(kNameSpaceID_None, nsGkAtoms::disabled));
- }
- return NS_OK;
- }
- bool
- HTMLButtonElement::RestoreState(nsPresState* aState)
- {
- if (aState && aState->IsDisabledSet()) {
- SetDisabled(aState->GetDisabled());
- }
- return false;
- }
- EventStates
- HTMLButtonElement::IntrinsicState() const
- {
- EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
-
- if (IsCandidateForConstraintValidation()) {
- if (IsValid()) {
- state |= NS_EVENT_STATE_VALID;
- if (!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
- state |= NS_EVENT_STATE_MOZ_UI_VALID;
- }
- } else {
- state |= NS_EVENT_STATE_INVALID;
- if (!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
- state |= NS_EVENT_STATE_MOZ_UI_INVALID;
- }
- }
- }
- if (mForm && !mForm->GetValidity() && IsSubmitControl()) {
- state |= NS_EVENT_STATE_MOZ_SUBMITINVALID;
- }
- return state;
- }
- JSObject*
- HTMLButtonElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
- {
- return HTMLButtonElementBinding::Wrap(aCx, this, aGivenProto);
- }
- } // namespace dom
- } // namespace mozilla
|