Source code

Revision control

Other Tools

1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
/**
6
* A factory to generate LoginForm objects that represent a set of login fields
7
* which aren't necessarily marked up with a <form> element.
8
*/
9
10
"use strict";
11
12
const EXPORTED_SYMBOLS = ["LoginFormFactory"];
13
14
const { XPCOMUtils } = ChromeUtils.import(
16
);
17
18
ChromeUtils.defineModuleGetter(
19
this,
20
"FormLikeFactory",
22
);
23
ChromeUtils.defineModuleGetter(
24
this,
25
"LoginHelper",
27
);
28
29
XPCOMUtils.defineLazyGetter(this, "log", () => {
30
return LoginHelper.createLogger("LoginFormFactory");
31
});
32
33
this.LoginFormFactory = {
34
/**
35
* WeakMap of the root element of a LoginForm to the LoginForm representing its fields.
36
*
37
* This is used to be able to lookup an existing LoginForm for a given root element since multiple
38
* calls to LoginFormFactory.createFrom* won't give the exact same object. When batching fills we don't always
39
* want to use the most recent list of elements for a LoginForm since we may end up doing multiple
40
* fills for the same set of elements when a field gets added between arming and running the
41
* DeferredTask.
42
*
43
* @type {WeakMap}
44
*/
45
_loginFormsByRootElement: new WeakMap(),
46
47
/**
48
* Maps all DOM content documents in this content process, including those in
49
* frames, to a WeakSet of LoginForm for the document.
50
*/
51
_loginFormRootElementsByDocument: new WeakMap(),
52
53
/**
54
* Create a LoginForm object from a <form>.
55
*
56
* @param {HTMLFormElement} aForm
57
* @return {LoginForm}
58
* @throws Error if aForm isn't an HTMLFormElement
59
*/
60
createFromForm(aForm) {
61
let formLike = FormLikeFactory.createFromForm(aForm);
62
formLike.action = LoginHelper.getFormActionOrigin(aForm);
63
64
let rootElementsSet = this.getRootElementsWeakSetForDocument(
65
formLike.ownerDocument
66
);
67
rootElementsSet.add(formLike.rootElement);
68
log.debug(
69
"adding",
70
formLike.rootElement,
71
"to root elements for",
72
formLike.ownerDocument
73
);
74
75
this._loginFormsByRootElement.set(formLike.rootElement, formLike);
76
return formLike;
77
},
78
79
/**
80
* Create a LoginForm object from a password or username field.
81
*
82
* If the field is in a <form>, construct the LoginForm from the form.
83
* Otherwise, create a LoginForm with a rootElement (wrapper) according to
84
* heuristics. Currently all <input> not in a <form> are one LoginForm but this
85
* shouldn't be relied upon as the heuristics may change to detect multiple
86
* "forms" (e.g. registration and login) on one page with a <form>.
87
*
88
* Note that two LoginForms created from the same field won't return the same LoginForm object.
89
* Use the `rootElement` property on the LoginForm as a key instead.
90
*
91
* @param {HTMLInputElement} aField - a password or username field in a document
92
* @return {LoginForm}
93
* @throws Error if aField isn't a password or username field in a document
94
*/
95
createFromField(aField) {
96
if (
97
ChromeUtils.getClassName(aField) !== "HTMLInputElement" ||
98
(aField.type != "password" && !LoginHelper.isUsernameFieldType(aField)) ||
99
!aField.ownerDocument
100
) {
101
throw new Error(
102
"createFromField requires a password or username field in a document"
103
);
104
}
105
106
if (aField.form) {
107
return this.createFromForm(aField.form);
108
} else if (aField.hasAttribute("form")) {
109
log.debug(
110
"createFromField: field has form attribute but no form: ",
111
aField.getAttribute("form")
112
);
113
}
114
115
let formLike = FormLikeFactory.createFromField(aField);
116
formLike.action = LoginHelper.getLoginOrigin(aField.ownerDocument.baseURI);
117
log.debug(
118
"Created non-form LoginForm for rootElement:",
119
aField.ownerDocument.documentElement
120
);
121
122
let rootElementsSet = this.getRootElementsWeakSetForDocument(
123
formLike.ownerDocument
124
);
125
rootElementsSet.add(formLike.rootElement);
126
log.debug(
127
"adding",
128
formLike.rootElement,
129
"to root elements for",
130
formLike.ownerDocument
131
);
132
133
this._loginFormsByRootElement.set(formLike.rootElement, formLike);
134
135
return formLike;
136
},
137
138
getRootElementsWeakSetForDocument(aDocument) {
139
let rootElementsSet = this._loginFormRootElementsByDocument.get(aDocument);
140
if (!rootElementsSet) {
141
rootElementsSet = new WeakSet();
142
this._loginFormRootElementsByDocument.set(aDocument, rootElementsSet);
143
}
144
return rootElementsSet;
145
},
146
147
getForRootElement(aRootElement) {
148
return this._loginFormsByRootElement.get(aRootElement);
149
},
150
151
setForRootElement(aRootElement, aLoginForm) {
152
return this._loginFormsByRootElement.set(aRootElement, aLoginForm);
153
},
154
};