-
Notifications
You must be signed in to change notification settings - Fork 3.9k
/
Copy pathdocument-submit.js
158 lines (141 loc) · 4.16 KB
/
document-submit.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import {ActionTrust_Enum} from '#core/constants/action-constants';
import {Services} from '#service';
import {dev, user, userAssert} from '#utils/log';
import {
SOURCE_ORIGIN_PARAM,
assertHttpsUrl,
checkCorsUrl,
isProxyOrigin,
} from './url';
/**
* @param {!./service/ampdoc-impl.AmpDoc} ampdoc
* @return {!Promise}
*/
export function installGlobalSubmitListenerForDoc(ampdoc) {
// Register global submit event listener only if the amp-form
// extension is used. Allowing the usage of native forms, otherwise.
return ampdoc.whenExtensionsKnown().then(() => {
if (ampdoc.declaresExtension('amp-form')) {
ampdoc
.getRootNode()
.addEventListener('submit', onDocumentFormSubmit_, true);
}
});
}
/**
* Intercept any submit on the current document and prevent invalid submits from
* going through.
*
* @param {!Event} e
*/
export function onDocumentFormSubmit_(e) {
if (e.defaultPrevented) {
return;
}
const form = dev().assertElement(e.target);
if (!form || form.tagName != 'FORM') {
return;
}
// amp-form extension will add novalidate to all forms to manually trigger
// validation. In that case `novalidate` doesn't have the same meaning.
const isAmpFormMarked = form.classList.contains('i-amphtml-form');
let shouldValidate;
if (isAmpFormMarked) {
shouldValidate = !form.hasAttribute('amp-novalidate');
} else {
shouldValidate = !form.hasAttribute('novalidate');
}
// Safari does not trigger validation check on submission, hence we
// trigger it manually. In other browsers this would never execute since
// the submit event wouldn't be fired if the form is invalid.
if (shouldValidate && form.checkValidity && !form.checkValidity()) {
e.preventDefault();
}
const inputs = form.elements;
for (let i = 0; i < inputs.length; i++) {
userAssert(
!inputs[i].name || inputs[i].name != SOURCE_ORIGIN_PARAM,
'Illegal input name, %s found: %s',
SOURCE_ORIGIN_PARAM,
inputs[i]
);
}
const action = form.getAttribute('action');
const actionXhr = form.getAttribute('action-xhr');
const method = (form.getAttribute('method') || 'GET').toUpperCase();
if (actionXhr) {
assertHttpsUrl(actionXhr, form, 'action-xhr');
userAssert(
!isProxyOrigin(actionXhr),
'form action-xhr should not be on AMP CDN: %s',
form
);
checkCorsUrl(actionXhr);
}
if (action) {
assertHttpsUrl(action, form, 'action');
userAssert(
!isProxyOrigin(action),
'form action should not be on AMP CDN: %s',
form
);
checkCorsUrl(action);
}
if (method == 'GET') {
userAssert(
actionXhr || action,
'form action-xhr or action attribute is required for method=GET: %s',
form
);
} else if (method == 'POST') {
if (action) {
const TAG = 'form';
user().error(
TAG,
'action attribute is invalid for method=POST: %s',
form
);
}
if (!actionXhr) {
e.preventDefault();
userAssert(
false,
'Only XHR based (via action-xhr attribute) submissions are support ' +
'for POST requests. %s',
form
);
}
}
const target = form.getAttribute('target');
if (target) {
userAssert(
target == '_blank' || target == '_top',
'form target=%s is invalid can only be _blank or _top: %s',
target,
form
);
} else {
form.setAttribute('target', '_top');
}
// For xhr submissions relay the submission event through action service to
// allow us to wait for amp-form (and possibly its dependencies) to execute
// the actual submission. For non-XHR GET we let the submission go through
// to allow _blank target to work.
if (actionXhr) {
e.preventDefault();
// It's important to stop propagation of the submission to avoid double
// handling of the event in cases were we are delegating to action service
// to deliver the submission event.
e.stopImmediatePropagation();
const actions = Services.actionServiceForDoc(form);
actions.execute(
form,
'submit',
/*args*/ null,
/*source*/ form,
/*caller*/ form,
e,
ActionTrust_Enum.HIGH
);
}
}