23
23
#include " base/bind.h"
24
24
#include " base/logging.h"
25
25
#include " base/strings/utf_string_conversions.h"
26
+ #include " chrome/browser/chrome_notification_types.h"
26
27
#include " content/public/browser/browser_thread.h"
28
+ #include " content/public/browser/notification_registrar.h"
29
+ #include " content/public/browser/notification_service.h"
27
30
#include " content/public/browser/resource_dispatcher_host.h"
28
31
#include " net/base/auth.h"
29
32
#include " net/url_request/url_request.h"
30
33
#include " ui/base/text/text_elider.h"
31
34
35
+ using content::BrowserThread;
36
+ using content::ResourceDispatcherHost;
37
+
38
+ namespace {
39
+ // Helper to remove the ref from an net::URLRequest to the ShellLoginDialog.
40
+ // Should only be called from the IO thread, since it accesses an
41
+ // net::URLRequest.
42
+ void ResetShellLoginDialogForRequest (net::URLRequest* request) {
43
+ ResourceDispatcherHost::Get ()->ClearLoginDelegateForRequest (request);
44
+ }
45
+
46
+ } // namespace
47
+
32
48
namespace content {
33
49
50
+ ShellLoginDialog::ShellLoginDialogList ShellLoginDialog::dialog_queue_;
51
+
34
52
ShellLoginDialog::ShellLoginDialog (
35
53
net::AuthChallengeInfo* auth_info,
36
54
net::URLRequest* request) : auth_info_(auth_info),
37
- request_ (request) {
55
+ request_ (request),
56
+ handled_auth_(false ) {
38
57
DCHECK (BrowserThread::CurrentlyOn (BrowserThread::IO));
39
58
BrowserThread::PostTask (
40
59
BrowserThread::UI, FROM_HERE,
@@ -53,6 +72,21 @@ void ShellLoginDialog::OnRequestCancelled() {
53
72
void ShellLoginDialog::UserAcceptedAuth (const string16& username,
54
73
const string16& password) {
55
74
DCHECK (BrowserThread::CurrentlyOn (BrowserThread::UI));
75
+
76
+ if (TestAndSetAuthHandled ())
77
+ return ;
78
+
79
+ // Calling NotifyAuthSupplied() directly instead of posting a task
80
+ // allows other ShellLoginDialog instances to queue their
81
+ // CloseContentsDeferred() before ours. Closing dialogs in the
82
+ // opposite order as they were created avoids races where remaining
83
+ // dialogs in the same tab may be briefly displayed to the user
84
+ // before they are removed.
85
+ NotifyAuthSupplied (username, password);
86
+
87
+ BrowserThread::PostTask (
88
+ BrowserThread::UI, FROM_HERE,
89
+ base::Bind (&ShellLoginDialog::CloseContentsDeferred, this ));
56
90
BrowserThread::PostTask (
57
91
BrowserThread::IO, FROM_HERE,
58
92
base::Bind (&ShellLoginDialog::SendAuthToRequester, this ,
@@ -61,13 +95,25 @@ void ShellLoginDialog::UserAcceptedAuth(const string16& username,
61
95
62
96
void ShellLoginDialog::UserCancelledAuth () {
63
97
DCHECK (BrowserThread::CurrentlyOn (BrowserThread::UI));
98
+ if (TestAndSetAuthHandled ())
99
+ return ;
100
+
101
+ // Similar to how we deal with notifications above in SetAuth()
102
+ if (BrowserThread::CurrentlyOn (BrowserThread::UI)) {
103
+ NotifyAuthCancelled ();
104
+ } else {
105
+ BrowserThread::PostTask (
106
+ BrowserThread::UI, FROM_HERE,
107
+ base::Bind (&ShellLoginDialog::NotifyAuthCancelled, this ));
108
+ }
109
+
64
110
BrowserThread::PostTask (
65
111
BrowserThread::IO, FROM_HERE,
66
112
base::Bind (&ShellLoginDialog::SendAuthToRequester, this ,
67
113
false , string16 (), string16 ()));
68
114
BrowserThread::PostTask (
69
115
BrowserThread::UI, FROM_HERE,
70
- base::Bind (&ShellLoginDialog::PlatformCleanUp , this ));
116
+ base::Bind (&ShellLoginDialog::CloseContentsDeferred , this ));
71
117
}
72
118
73
119
ShellLoginDialog::~ShellLoginDialog () {
@@ -93,16 +139,38 @@ void ShellLoginDialog::PrepDialog(const string16& host,
93
139
explanation += ASCIIToUTF16 (" ." );
94
140
}
95
141
142
+ AddObservers ();
143
+ dialog_queue_.push_back (this );
96
144
PlatformCreateDialog (explanation);
145
+ if (dialog_queue_.size () == 1 )
146
+ PlatformShowDialog ();
147
+ }
148
+
149
+ void ShellLoginDialog::HandleQueueOnClose ()
150
+ {
151
+ DCHECK (BrowserThread::CurrentlyOn (BrowserThread::UI));
152
+ ShellLoginDialogList::iterator i (
153
+ std::find (dialog_queue_.begin (), dialog_queue_.end (), this ));
154
+
155
+ // Ignore the second invocation.
156
+ if (i == dialog_queue_.end ())
157
+ return ;
158
+
159
+ bool removed_topmost_dialog = i == dialog_queue_.begin ();
160
+ dialog_queue_.erase (i);
161
+ if (!dialog_queue_.empty () && removed_topmost_dialog) {
162
+ ShellLoginDialog* next_dlg = dialog_queue_.front ();
163
+ next_dlg->PlatformShowDialog ();
164
+ }
97
165
}
98
166
99
167
void ShellLoginDialog::SendAuthToRequester (bool success,
100
168
const string16& username,
101
169
const string16& password) {
102
170
DCHECK (BrowserThread::CurrentlyOn (BrowserThread::IO));
103
- if (success)
171
+ if (success) {
104
172
request_->SetAuth (net::AuthCredentials (username, password));
105
- else
173
+ } else
106
174
request_->CancelAuth ();
107
175
ResourceDispatcherHost::Get ()->ClearLoginDelegateForRequest (request_);
108
176
@@ -111,4 +179,149 @@ void ShellLoginDialog::SendAuthToRequester(bool success,
111
179
base::Bind (&ShellLoginDialog::PlatformCleanUp, this ));
112
180
}
113
181
182
+ void ShellLoginDialog::NotifyAuthSupplied (const string16& username,
183
+ const string16& password) {
184
+
185
+ DCHECK (BrowserThread::CurrentlyOn (BrowserThread::UI));
186
+
187
+ content::NotificationService* service =
188
+ content::NotificationService::current ();
189
+
190
+ AuthSuppliedLoginNotificationDetails details (this , username, password);
191
+
192
+ service->Notify (chrome::NOTIFICATION_AUTH_SUPPLIED,
193
+ content::Source<ShellLoginDialog>(this ),
194
+ content::Details<AuthSuppliedLoginNotificationDetails>(&details));
195
+ }
196
+
197
+ void ShellLoginDialog::NotifyAuthCancelled () {
198
+ DCHECK (BrowserThread::CurrentlyOn (BrowserThread::UI));
199
+ DCHECK (WasAuthHandled ());
200
+
201
+ content::NotificationService* service =
202
+ content::NotificationService::current ();
203
+
204
+ AuthSuppliedLoginNotificationDetails details (this , string16 (), string16 ());
205
+
206
+ service->Notify (chrome::NOTIFICATION_AUTH_CANCELLED,
207
+ content::Source<ShellLoginDialog>(this ),
208
+ content::Details<LoginNotificationDetails>(&details));
209
+ }
210
+
211
+
212
+ void ShellLoginDialog::Observe (int type,
213
+ const content::NotificationSource& source,
214
+ const content::NotificationDetails& details) {
215
+ DCHECK (BrowserThread::CurrentlyOn (BrowserThread::UI));
216
+ DCHECK (type == chrome::NOTIFICATION_AUTH_SUPPLIED ||
217
+ type == chrome::NOTIFICATION_AUTH_CANCELLED);
218
+
219
+ // Break out early if we aren't interested in the notification.
220
+ if (WasAuthHandled ())
221
+ return ;
222
+
223
+ LoginNotificationDetails* login_details =
224
+ content::Details<LoginNotificationDetails>(details).ptr ();
225
+
226
+ // WasAuthHandled() should always test positive before we publish
227
+ // AUTH_SUPPLIED or AUTH_CANCELLED notifications.
228
+ DCHECK (login_details->handler () != this );
229
+
230
+ // Only handle notification for the identical auth info.
231
+ if (!login_details->handler ()->auth_info ()->Equals (*auth_info ()))
232
+ return ;
233
+
234
+ // Set or cancel the auth in this handler.
235
+ if (type == chrome::NOTIFICATION_AUTH_SUPPLIED) {
236
+ AuthSuppliedLoginNotificationDetails* supplied_details =
237
+ content::Details<AuthSuppliedLoginNotificationDetails>(details).ptr ();
238
+ UserAcceptedAuth (supplied_details->username (), supplied_details->password ());
239
+ } else {
240
+ DCHECK (type == chrome::NOTIFICATION_AUTH_CANCELLED);
241
+ UserCancelledAuth ();
242
+ }
243
+ }
244
+
245
+ // Returns whether authentication had been handled (SetAuth or CancelAuth).
246
+ bool ShellLoginDialog::WasAuthHandled () const {
247
+ base::AutoLock lock (handled_auth_lock_);
248
+ bool was_handled = handled_auth_;
249
+ return was_handled;
250
+ }
251
+
252
+ // Marks authentication as handled and returns the previous handled state.
253
+ bool ShellLoginDialog::TestAndSetAuthHandled () {
254
+ base::AutoLock lock (handled_auth_lock_);
255
+ bool was_handled = handled_auth_;
256
+ handled_auth_ = true ;
257
+ return was_handled;
258
+ }
259
+
260
+ // Closes the view_contents from the UI loop.
261
+ void ShellLoginDialog::CloseContentsDeferred () {
262
+ DCHECK (BrowserThread::CurrentlyOn (BrowserThread::UI));
263
+
264
+ HandleQueueOnClose ();
265
+ PlatformCleanUp ();
266
+ }
267
+
268
+ void ShellLoginDialog::AddObservers () {
269
+ DCHECK (BrowserThread::CurrentlyOn (BrowserThread::UI));
270
+
271
+ // This is probably OK; we need to listen to everything and we break out of
272
+ // the Observe() if we aren't handling the same auth_info().
273
+ registrar_.reset (new content::NotificationRegistrar);
274
+ registrar_->Add (this , chrome::NOTIFICATION_AUTH_SUPPLIED,
275
+ content::NotificationService::AllBrowserContextsAndSources ());
276
+ registrar_->Add (this , chrome::NOTIFICATION_AUTH_CANCELLED,
277
+ content::NotificationService::AllBrowserContextsAndSources ());
278
+ }
279
+
280
+ void ShellLoginDialog::RemoveObservers () {
281
+ DCHECK (BrowserThread::CurrentlyOn (BrowserThread::UI));
282
+
283
+ registrar_.reset ();
284
+ }
285
+
286
+ void ShellLoginDialog::ReleaseSoon () {
287
+ if (!TestAndSetAuthHandled ()) {
288
+ BrowserThread::PostTask (
289
+ BrowserThread::IO, FROM_HERE,
290
+ base::Bind (&ShellLoginDialog::CancelAuthDeferred, this ));
291
+ BrowserThread::PostTask (
292
+ BrowserThread::UI, FROM_HERE,
293
+ base::Bind (&ShellLoginDialog::NotifyAuthCancelled, this ));
294
+ }
295
+
296
+ BrowserThread::PostTask (
297
+ BrowserThread::UI, FROM_HERE,
298
+ base::Bind (&ShellLoginDialog::RemoveObservers, this ));
299
+
300
+ // Delete this object once all InvokeLaters have been called.
301
+ BrowserThread::ReleaseSoon (BrowserThread::IO, FROM_HERE, this );
302
+ }
303
+
304
+ // Calls SetAuth from the IO loop.
305
+ void ShellLoginDialog::SetAuthDeferred (const string16& username,
306
+ const string16& password) {
307
+ DCHECK (BrowserThread::CurrentlyOn (BrowserThread::IO));
308
+
309
+ if (request_) {
310
+ request_->SetAuth (net::AuthCredentials (username, password));
311
+ ResetShellLoginDialogForRequest (request_);
312
+ }
313
+ }
314
+
315
+ // Calls CancelAuth from the IO loop.
316
+ void ShellLoginDialog::CancelAuthDeferred () {
317
+ DCHECK (BrowserThread::CurrentlyOn (BrowserThread::IO));
318
+
319
+ if (request_) {
320
+ request_->CancelAuth ();
321
+ // Verify that CancelAuth doesn't destroy the request via our delegate.
322
+ DCHECK (request_ != NULL );
323
+ ResetShellLoginDialogForRequest (request_);
324
+ }
325
+ }
326
+
114
327
} // namespace content
0 commit comments