92b558df29be12d53c9a494083abf37628db4c84
[WebKit-https.git] / Source / WebKit / blackberry / WebCoreSupport / CredentialTransformData.cpp
1 /*
2  * Copyright (C) 2009 Google Inc. All rights reserved.
3  * Copyright (C) 2012 Research In Motion Limited. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32  /*
33  * This methods are based on Chromium codes in
34  * Source/WebKit/chromium/src/WebPasswordFormUtils.cpp
35  */
36
37 #include "config.h"
38 #include "CredentialTransformData.h"
39
40 #include "HTMLInputElement.h"
41 #include "KURL.h"
42 #include <wtf/Vector.h>
43
44 namespace WebCore {
45
46 namespace {
47 // Maximum number of password fields we will observe before throwing our
48 // hands in the air and giving up with a given form.
49 static const size_t maxPasswords = 3;
50
51 // Helper method to clear url of unneeded parts.
52 KURL stripURL(const KURL& url)
53 {
54     KURL strippedURL = url;
55     strippedURL.setUser(String());
56     strippedURL.setPass(String());
57     strippedURL.setQuery(String());
58     strippedURL.setFragmentIdentifier(String());
59     return strippedURL;
60 }
61
62 // Helper method to determine which password is the main one, and which is
63 // an old password (e.g on a "make new password" form), if any.
64 bool locateSpecificPasswords(Vector<HTMLInputElement*>& passwords, HTMLInputElement** password, HTMLInputElement** oldPassword)
65 {
66     ASSERT(password);
67     ASSERT(oldPassword);
68
69     switch (passwords.size()) {
70     case 1:
71         // Single password, easy.
72         *password = passwords[0];
73         break;
74     case 2:
75         if (passwords[0]->value() == passwords[1]->value())
76             // Treat two identical passwords as a single password.
77             *password = passwords[0];
78         else {
79             // Assume first is old password, second is new (no choice but to guess).
80             *oldPassword = passwords[0];
81             *password = passwords[1];
82         }
83         break;
84     case 3:
85         if (passwords[0]->value() == passwords[1]->value()
86             && passwords[0]->value() == passwords[2]->value()) {
87             // All three passwords the same? Just treat as one and hope.
88             *password = passwords[0];
89         } else if (passwords[0]->value() == passwords[1]->value()) {
90             // Two the same and one different -> old password is duplicated one.
91             *oldPassword = passwords[0];
92             *password = passwords[2];
93         } else if (passwords[1]->value() == passwords[2]->value()) {
94             *oldPassword = passwords[0];
95             *password = passwords[1];
96         }
97         else {
98             // Three different passwords, or first and last match with middle
99             // different. No idea which is which, so no luck.
100             return false;
101         }
102         break;
103     default:
104         return false;
105     }
106     return true;
107 }
108
109 } // namespace
110
111 CredentialTransformData::CredentialTransformData()
112     : m_userNameElement(0)
113     , m_passwordElement(0)
114     , m_oldPasswordElement(0)
115     , m_isValid(false)
116 {
117 }
118
119 CredentialTransformData::CredentialTransformData(HTMLFormElement* form, bool isForSaving)
120     : m_userNameElement(0)
121     , m_passwordElement(0)
122     , m_oldPasswordElement(0)
123     , m_isValid(false)
124 {
125     ASSERT(form);
126
127     // Get the document URL
128     KURL fullOrigin(ParsedURLString, form->document()->documentURI());
129
130     // Calculate the canonical action URL
131     String action = form->action();
132     if (action.isNull())
133         action = ""; // missing 'action' attribute implies current URL
134     KURL fullAction = form->document()->completeURL(action);
135     if (!fullAction.isValid())
136         return;
137
138     if (!findPasswordFormFields(form))
139         return;
140
141     // Won't restore password if there're two password inputs on the page.
142     if (!isForSaving && m_oldPasswordElement)
143         return;
144
145     KURL url = stripURL(fullOrigin);
146     m_action = stripURL(fullAction);
147     m_protectionSpace = ProtectionSpace(url.host(), url.port(), ProtectionSpaceServerHTTP, "Form", ProtectionSpaceAuthenticationSchemeHTMLForm);
148     m_credential = Credential(m_userNameElement->value(), m_passwordElement->value(), CredentialPersistencePermanent);
149
150     m_isValid = true;
151 }
152
153 CredentialTransformData::CredentialTransformData(const ProtectionSpace& protectionSpace, const Credential& credential)
154     : m_protectionSpace(protectionSpace)
155     , m_credential(credential)
156     , m_userNameElement(0)
157     , m_passwordElement(0)
158     , m_oldPasswordElement(0)
159     , m_isValid(true)
160 {
161 }
162
163 Credential CredentialTransformData::credential() const
164 {
165     if (m_credential.isEmpty() && m_userNameElement && m_passwordElement)
166         return m_credential = Credential(m_userNameElement->value(), m_passwordElement->value(), CredentialPersistencePermanent);
167     return m_credential;
168 }
169
170 void CredentialTransformData::setCredential(const Credential& credential)
171 {
172     if (!m_isValid)
173         return;
174
175     m_credential = credential;
176     m_userNameElement->setValue(credential.user());
177     m_userNameElement->setAutofilled();
178     m_passwordElement->setValue(credential.password());
179     m_passwordElement->setAutofilled();
180 }
181
182 bool CredentialTransformData::findPasswordFormFields(HTMLFormElement* form)
183 {
184     ASSERT(form);
185
186     int firstPasswordIndex = 0;
187     // First, find the password fields and activated submit button.
188     const Vector<FormAssociatedElement*>& formElements = form->associatedElements();
189     Vector<HTMLInputElement*> passwords;
190     for (size_t i = 0; i < formElements.size(); i++) {
191         if (!formElements[i]->isFormControlElement())
192             continue;
193         HTMLFormControlElement* formElement = static_cast<HTMLFormControlElement*>(formElements[i]);
194         if (!formElement->hasTagName(HTMLNames::inputTag))
195             continue;
196
197         HTMLInputElement* inputElement = formElement->toInputElement();
198         if (!inputElement->isEnabledFormControl())
199             continue;
200
201         if ((passwords.size() < maxPasswords)
202             && inputElement->isPasswordField()
203             && inputElement->shouldAutocomplete()) {
204             if (passwords.isEmpty())
205                 firstPasswordIndex = i;
206             passwords.append(inputElement);
207         }
208     }
209
210     if (!passwords.isEmpty()) {
211         // Then, search backwards for the username field.
212         for (int i = firstPasswordIndex - 1; i >= 0; i--) {
213             if (!formElements[i]->isFormControlElement())
214                 continue;
215             HTMLFormControlElement* formElement = static_cast<HTMLFormControlElement*>(formElements[i]);
216             if (!formElement->hasTagName(HTMLNames::inputTag))
217                 continue;
218
219             HTMLInputElement* inputElement = formElement->toInputElement();
220             if (!inputElement->isEnabledFormControl())
221                 continue;
222
223             // Various input types such as text, url, email can be a username field.
224             if ((inputElement->isTextField() && !inputElement->isPasswordField())
225                 && (inputElement->shouldAutocomplete())) {
226                 m_userNameElement = inputElement;
227                 break;
228             }
229         }
230     }
231     if (!m_userNameElement)
232         return false;
233
234     if (!locateSpecificPasswords(passwords, &m_passwordElement, &m_oldPasswordElement))
235         return false;
236     return true;
237 }
238
239 } // namespace WebCore