HTMLButtonElement.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "mozilla/dom/HTMLButtonElement.h"
  6. #include "HTMLFormSubmissionConstants.h"
  7. #include "mozilla/dom/HTMLButtonElementBinding.h"
  8. #include "mozilla/dom/HTMLFormSubmission.h"
  9. #include "nsIDOMHTMLFormElement.h"
  10. #include "nsAttrValueInlines.h"
  11. #include "nsGkAtoms.h"
  12. #include "nsIPresShell.h"
  13. #include "nsStyleConsts.h"
  14. #include "nsPresContext.h"
  15. #include "nsIFormControl.h"
  16. #include "nsIURL.h"
  17. #include "nsIFrame.h"
  18. #include "nsIFormControlFrame.h"
  19. #include "nsIDOMEvent.h"
  20. #include "nsIDocument.h"
  21. #include "mozilla/ContentEvents.h"
  22. #include "mozilla/EventDispatcher.h"
  23. #include "mozilla/EventStateManager.h"
  24. #include "mozilla/EventStates.h"
  25. #include "mozilla/MouseEvents.h"
  26. #include "mozilla/TextEvents.h"
  27. #include "nsUnicharUtils.h"
  28. #include "nsLayoutUtils.h"
  29. #include "nsPresState.h"
  30. #include "nsError.h"
  31. #include "nsFocusManager.h"
  32. #include "mozilla/dom/HTMLFormElement.h"
  33. #include "mozAutoDocUpdate.h"
  34. #define NS_IN_SUBMIT_CLICK (1 << 0)
  35. #define NS_OUTER_ACTIVATE_EVENT (1 << 1)
  36. NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Button)
  37. namespace mozilla {
  38. namespace dom {
  39. static const nsAttrValue::EnumTable kButtonTypeTable[] = {
  40. { "button", NS_FORM_BUTTON_BUTTON },
  41. { "reset", NS_FORM_BUTTON_RESET },
  42. { "submit", NS_FORM_BUTTON_SUBMIT },
  43. { nullptr, 0 }
  44. };
  45. // Default type is 'submit'.
  46. static const nsAttrValue::EnumTable* kButtonDefaultType = &kButtonTypeTable[2];
  47. // Construction, destruction
  48. HTMLButtonElement::HTMLButtonElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
  49. FromParser aFromParser)
  50. : nsGenericHTMLFormElementWithState(aNodeInfo),
  51. mType(kButtonDefaultType->value),
  52. mDisabledChanged(false),
  53. mInInternalActivate(false),
  54. mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT))
  55. {
  56. // Set up our default state: enabled
  57. AddStatesSilently(NS_EVENT_STATE_ENABLED);
  58. }
  59. HTMLButtonElement::~HTMLButtonElement()
  60. {
  61. }
  62. // nsISupports
  63. NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLButtonElement,
  64. nsGenericHTMLFormElementWithState,
  65. mValidity)
  66. NS_IMPL_ADDREF_INHERITED(HTMLButtonElement, Element)
  67. NS_IMPL_RELEASE_INHERITED(HTMLButtonElement, Element)
  68. // QueryInterface implementation for HTMLButtonElement
  69. NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLButtonElement)
  70. NS_INTERFACE_TABLE_INHERITED(HTMLButtonElement,
  71. nsIDOMHTMLButtonElement,
  72. nsIConstraintValidation)
  73. NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLFormElementWithState)
  74. // nsIConstraintValidation
  75. NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_SETCUSTOMVALIDITY(HTMLButtonElement)
  76. NS_IMETHODIMP
  77. HTMLButtonElement::SetCustomValidity(const nsAString& aError)
  78. {
  79. nsIConstraintValidation::SetCustomValidity(aError);
  80. UpdateState(true);
  81. return NS_OK;
  82. }
  83. void
  84. HTMLButtonElement::UpdateBarredFromConstraintValidation()
  85. {
  86. SetBarredFromConstraintValidation(mType == NS_FORM_BUTTON_BUTTON ||
  87. mType == NS_FORM_BUTTON_RESET ||
  88. IsDisabled());
  89. }
  90. void
  91. HTMLButtonElement::FieldSetDisabledChanged(bool aNotify)
  92. {
  93. UpdateBarredFromConstraintValidation();
  94. nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
  95. }
  96. // nsIDOMHTMLButtonElement
  97. NS_IMPL_ELEMENT_CLONE(HTMLButtonElement)
  98. // nsIDOMHTMLButtonElement
  99. NS_IMETHODIMP
  100. HTMLButtonElement::GetForm(nsIDOMHTMLFormElement** aForm)
  101. {
  102. return nsGenericHTMLFormElementWithState::GetForm(aForm);
  103. }
  104. NS_IMPL_BOOL_ATTR(HTMLButtonElement, Autofocus, autofocus)
  105. NS_IMPL_BOOL_ATTR(HTMLButtonElement, Disabled, disabled)
  106. NS_IMPL_ACTION_ATTR(HTMLButtonElement, FormAction, formaction)
  107. NS_IMPL_ENUM_ATTR_DEFAULT_MISSING_INVALID_VALUES(HTMLButtonElement, FormEnctype, formenctype,
  108. "", kFormDefaultEnctype->tag)
  109. NS_IMPL_ENUM_ATTR_DEFAULT_MISSING_INVALID_VALUES(HTMLButtonElement, FormMethod, formmethod,
  110. "", kFormDefaultMethod->tag)
  111. NS_IMPL_BOOL_ATTR(HTMLButtonElement, FormNoValidate, formnovalidate)
  112. NS_IMPL_STRING_ATTR(HTMLButtonElement, FormTarget, formtarget)
  113. NS_IMPL_STRING_ATTR(HTMLButtonElement, Name, name)
  114. NS_IMPL_STRING_ATTR(HTMLButtonElement, Value, value)
  115. NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLButtonElement, Type, type,
  116. kButtonDefaultType->tag)
  117. int32_t
  118. HTMLButtonElement::TabIndexDefault()
  119. {
  120. return 0;
  121. }
  122. bool
  123. HTMLButtonElement::IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex)
  124. {
  125. if (nsGenericHTMLFormElementWithState::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
  126. return true;
  127. }
  128. *aIsFocusable = !IsDisabled();
  129. return false;
  130. }
  131. bool
  132. HTMLButtonElement::ParseAttribute(int32_t aNamespaceID,
  133. nsIAtom* aAttribute,
  134. const nsAString& aValue,
  135. nsAttrValue& aResult)
  136. {
  137. if (aNamespaceID == kNameSpaceID_None) {
  138. if (aAttribute == nsGkAtoms::type) {
  139. // XXX ARG!! This is major evilness. ParseAttribute
  140. // shouldn't set members. Override SetAttr instead
  141. bool success = aResult.ParseEnumValue(aValue, kButtonTypeTable, false);
  142. if (success) {
  143. mType = aResult.GetEnumValue();
  144. } else {
  145. mType = kButtonDefaultType->value;
  146. }
  147. return success;
  148. }
  149. if (aAttribute == nsGkAtoms::formmethod) {
  150. return aResult.ParseEnumValue(aValue, kFormMethodTable, false);
  151. }
  152. if (aAttribute == nsGkAtoms::formenctype) {
  153. return aResult.ParseEnumValue(aValue, kFormEnctypeTable, false);
  154. }
  155. }
  156. return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
  157. aResult);
  158. }
  159. bool
  160. HTMLButtonElement::IsDisabledForEvents(WidgetEvent* aEvent)
  161. {
  162. nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
  163. nsIFrame* formFrame = do_QueryFrame(formControlFrame);
  164. return IsElementDisabledForEvents(aEvent, formFrame);
  165. }
  166. nsresult
  167. HTMLButtonElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
  168. {
  169. aVisitor.mCanHandle = false;
  170. if (IsDisabledForEvents(aVisitor.mEvent)) {
  171. return NS_OK;
  172. }
  173. // Track whether we're in the outermost Dispatch invocation that will
  174. // cause activation of the input. That is, if we're a click event, or a
  175. // DOMActivate that was dispatched directly, this will be set, but if we're
  176. // a DOMActivate dispatched from click handling, it will not be set.
  177. WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
  178. bool outerActivateEvent =
  179. ((mouseEvent && mouseEvent->IsLeftClickEvent()) ||
  180. (aVisitor.mEvent->mMessage == eLegacyDOMActivate &&
  181. !mInInternalActivate));
  182. if (outerActivateEvent) {
  183. aVisitor.mItemFlags |= NS_OUTER_ACTIVATE_EVENT;
  184. if (mType == NS_FORM_BUTTON_SUBMIT && mForm) {
  185. aVisitor.mItemFlags |= NS_IN_SUBMIT_CLICK;
  186. // tell the form that we are about to enter a click handler.
  187. // that means that if there are scripted submissions, the
  188. // latest one will be deferred until after the exit point of the handler.
  189. mForm->OnSubmitClickBegin(this);
  190. }
  191. }
  192. return nsGenericHTMLElement::GetEventTargetParent(aVisitor);
  193. }
  194. nsresult
  195. HTMLButtonElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
  196. {
  197. nsresult rv = NS_OK;
  198. if (!aVisitor.mPresContext) {
  199. return rv;
  200. }
  201. if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
  202. WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
  203. if (mouseEvent && mouseEvent->IsLeftClickEvent()) {
  204. // DOMActive event should be trusted since the activation is actually
  205. // occurred even if the cause is an untrusted click event.
  206. InternalUIEvent actEvent(true, eLegacyDOMActivate, mouseEvent);
  207. actEvent.mDetail = 1;
  208. nsCOMPtr<nsIPresShell> shell = aVisitor.mPresContext->GetPresShell();
  209. if (shell) {
  210. nsEventStatus status = nsEventStatus_eIgnore;
  211. mInInternalActivate = true;
  212. shell->HandleDOMEventWithTarget(this, &actEvent, &status);
  213. mInInternalActivate = false;
  214. // If activate is cancelled, we must do the same as when click is
  215. // cancelled (revert the checkbox to its original value).
  216. if (status == nsEventStatus_eConsumeNoDefault) {
  217. aVisitor.mEventStatus = status;
  218. }
  219. }
  220. }
  221. }
  222. // mForm is null if the event handler removed us from the document (bug 194582).
  223. if ((aVisitor.mItemFlags & NS_IN_SUBMIT_CLICK) && mForm) {
  224. // tell the form that we are about to exit a click handler
  225. // so the form knows not to defer subsequent submissions
  226. // the pending ones that were created during the handler
  227. // will be flushed or forgoten.
  228. mForm->OnSubmitClickEnd();
  229. }
  230. if (nsEventStatus_eIgnore == aVisitor.mEventStatus) {
  231. switch (aVisitor.mEvent->mMessage) {
  232. case eKeyPress:
  233. case eKeyUp:
  234. {
  235. // For backwards compat, trigger buttons with space or enter
  236. // (bug 25300)
  237. WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
  238. if ((keyEvent->mKeyCode == NS_VK_RETURN &&
  239. eKeyPress == aVisitor.mEvent->mMessage) ||
  240. (keyEvent->mKeyCode == NS_VK_SPACE &&
  241. eKeyUp == aVisitor.mEvent->mMessage)) {
  242. DispatchSimulatedClick(this, aVisitor.mEvent->IsTrusted(),
  243. aVisitor.mPresContext);
  244. aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
  245. }
  246. }
  247. break;
  248. default:
  249. break;
  250. }
  251. if (aVisitor.mItemFlags & NS_OUTER_ACTIVATE_EVENT) {
  252. if (mForm && (mType == NS_FORM_BUTTON_SUBMIT ||
  253. mType == NS_FORM_BUTTON_RESET)) {
  254. InternalFormEvent event(true,
  255. (mType == NS_FORM_BUTTON_RESET) ? eFormReset : eFormSubmit);
  256. event.mOriginator = this;
  257. nsEventStatus status = nsEventStatus_eIgnore;
  258. nsCOMPtr<nsIPresShell> presShell =
  259. aVisitor.mPresContext->GetPresShell();
  260. // If |nsIPresShell::Destroy| has been called due to
  261. // handling the event, the pres context will return
  262. // a null pres shell. See bug 125624.
  263. //
  264. // Using presShell to dispatch the event. It makes sure that
  265. // event is not handled if the window is being destroyed.
  266. if (presShell && (event.mMessage != eFormSubmit ||
  267. mForm->SubmissionCanProceed(this))) {
  268. // TODO: removing this code and have the submit event sent by the form
  269. // see bug 592124.
  270. // Hold a strong ref while dispatching
  271. RefPtr<HTMLFormElement> form(mForm);
  272. presShell->HandleDOMEventWithTarget(form, &event, &status);
  273. aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
  274. }
  275. }
  276. }
  277. } else if ((aVisitor.mItemFlags & NS_IN_SUBMIT_CLICK) && mForm) {
  278. // Tell the form to flush a possible pending submission.
  279. // the reason is that the script returned false (the event was
  280. // not ignored) so if there is a stored submission, it needs to
  281. // be submitted immediatelly.
  282. // Note, NS_IN_SUBMIT_CLICK is set only when we're in outer activate event.
  283. mForm->FlushPendingSubmission();
  284. } //if
  285. return rv;
  286. }
  287. nsresult
  288. HTMLButtonElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
  289. nsIContent* aBindingParent,
  290. bool aCompileEventHandlers)
  291. {
  292. nsresult rv =
  293. nsGenericHTMLFormElementWithState::BindToTree(aDocument, aParent, aBindingParent,
  294. aCompileEventHandlers);
  295. NS_ENSURE_SUCCESS(rv, rv);
  296. // Update our state; we may now be the default submit element
  297. UpdateState(false);
  298. return NS_OK;
  299. }
  300. void
  301. HTMLButtonElement::UnbindFromTree(bool aDeep, bool aNullParent)
  302. {
  303. nsGenericHTMLFormElementWithState::UnbindFromTree(aDeep, aNullParent);
  304. // Update our state; we may no longer be the default submit element
  305. UpdateState(false);
  306. }
  307. NS_IMETHODIMP
  308. HTMLButtonElement::Reset()
  309. {
  310. return NS_OK;
  311. }
  312. NS_IMETHODIMP
  313. HTMLButtonElement::SubmitNamesValues(HTMLFormSubmission* aFormSubmission)
  314. {
  315. //
  316. // We only submit if we were the button pressed
  317. //
  318. if (aFormSubmission->GetOriginatingElement() != this) {
  319. return NS_OK;
  320. }
  321. // Disabled elements don't submit
  322. if (IsDisabled()) {
  323. return NS_OK;
  324. }
  325. //
  326. // Get the name (if no name, no submit)
  327. //
  328. nsAutoString name;
  329. GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
  330. if (name.IsEmpty()) {
  331. return NS_OK;
  332. }
  333. //
  334. // Get the value
  335. //
  336. nsAutoString value;
  337. nsresult rv = GetValue(value);
  338. if (NS_FAILED(rv)) {
  339. return rv;
  340. }
  341. //
  342. // Submit
  343. //
  344. return aFormSubmission->AddNameValuePair(name, value);
  345. }
  346. void
  347. HTMLButtonElement::DoneCreatingElement()
  348. {
  349. if (!mInhibitStateRestoration) {
  350. nsresult rv = GenerateStateKey();
  351. if (NS_SUCCEEDED(rv)) {
  352. RestoreFormControlState();
  353. }
  354. }
  355. }
  356. nsresult
  357. HTMLButtonElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
  358. const nsAttrValueOrString* aValue,
  359. bool aNotify)
  360. {
  361. if (aNotify && aName == nsGkAtoms::disabled &&
  362. aNameSpaceID == kNameSpaceID_None) {
  363. mDisabledChanged = true;
  364. }
  365. return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName,
  366. aValue, aNotify);
  367. }
  368. nsresult
  369. HTMLButtonElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
  370. const nsAttrValue* aValue,
  371. const nsAttrValue* aOldValue, bool aNotify)
  372. {
  373. if (aNameSpaceID == kNameSpaceID_None) {
  374. if (aName == nsGkAtoms::type) {
  375. if (!aValue) {
  376. mType = kButtonDefaultType->value;
  377. }
  378. }
  379. if (aName == nsGkAtoms::type || aName == nsGkAtoms::disabled) {
  380. UpdateBarredFromConstraintValidation();
  381. }
  382. }
  383. return nsGenericHTMLFormElementWithState::AfterSetAttr(aNameSpaceID, aName,
  384. aValue, aOldValue,
  385. aNotify);
  386. }
  387. NS_IMETHODIMP
  388. HTMLButtonElement::SaveState()
  389. {
  390. if (!mDisabledChanged) {
  391. return NS_OK;
  392. }
  393. nsPresState* state = GetPrimaryPresState();
  394. if (state) {
  395. // We do not want to save the real disabled state but the disabled
  396. // attribute.
  397. state->SetDisabled(HasAttr(kNameSpaceID_None, nsGkAtoms::disabled));
  398. }
  399. return NS_OK;
  400. }
  401. bool
  402. HTMLButtonElement::RestoreState(nsPresState* aState)
  403. {
  404. if (aState && aState->IsDisabledSet()) {
  405. SetDisabled(aState->GetDisabled());
  406. }
  407. return false;
  408. }
  409. EventStates
  410. HTMLButtonElement::IntrinsicState() const
  411. {
  412. EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
  413. if (IsCandidateForConstraintValidation()) {
  414. if (IsValid()) {
  415. state |= NS_EVENT_STATE_VALID;
  416. if (!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
  417. state |= NS_EVENT_STATE_MOZ_UI_VALID;
  418. }
  419. } else {
  420. state |= NS_EVENT_STATE_INVALID;
  421. if (!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
  422. state |= NS_EVENT_STATE_MOZ_UI_INVALID;
  423. }
  424. }
  425. }
  426. if (mForm && !mForm->GetValidity() && IsSubmitControl()) {
  427. state |= NS_EVENT_STATE_MOZ_SUBMITINVALID;
  428. }
  429. return state;
  430. }
  431. JSObject*
  432. HTMLButtonElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
  433. {
  434. return HTMLButtonElementBinding::Wrap(aCx, this, aGivenProto);
  435. }
  436. } // namespace dom
  437. } // namespace mozilla