OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/api/identity/web_auth_flow.h" | 5 #include "chrome/browser/extensions/api/identity/web_auth_flow.h" |
6 | 6 |
| 7 #include "base/base64.h" |
7 #include "base/location.h" | 8 #include "base/location.h" |
| 9 #include "base/string_util.h" |
| 10 #include "chrome/browser/extensions/event_router.h" |
| 11 #include "chrome/browser/extensions/extension_system.h" |
8 #include "chrome/browser/profiles/profile.h" | 12 #include "chrome/browser/profiles/profile.h" |
9 #include "chrome/browser/ui/browser.h" | 13 #include "chrome/browser/ui/browser.h" |
10 #include "chrome/browser/ui/browser_navigator.h" | 14 #include "chrome/browser/ui/browser_navigator.h" |
| 15 #include "chrome/browser/ui/extensions/shell_window.h" |
| 16 #include "chrome/common/extensions/extension_constants.h" |
11 #include "content/public/browser/load_notification_details.h" | 17 #include "content/public/browser/load_notification_details.h" |
12 #include "content/public/browser/navigation_controller.h" | 18 #include "content/public/browser/navigation_controller.h" |
13 #include "content/public/browser/notification_details.h" | 19 #include "content/public/browser/notification_details.h" |
14 #include "content/public/browser/notification_source.h" | 20 #include "content/public/browser/notification_source.h" |
15 #include "content/public/browser/notification_types.h" | 21 #include "content/public/browser/notification_types.h" |
| 22 #include "content/public/browser/render_process_host.h" |
| 23 #include "content/public/browser/render_view_host.h" |
16 #include "content/public/browser/resource_request_details.h" | 24 #include "content/public/browser/resource_request_details.h" |
17 #include "content/public/browser/web_contents.h" | 25 #include "content/public/browser/web_contents.h" |
18 #include "content/public/common/page_transition_types.h" | 26 #include "content/public/common/page_transition_types.h" |
| 27 #include "crypto/random.h" |
19 #include "googleurl/src/gurl.h" | 28 #include "googleurl/src/gurl.h" |
20 #include "ui/base/window_open_disposition.h" | 29 #include "ui/base/window_open_disposition.h" |
21 | 30 |
22 using content::LoadNotificationDetails; | 31 using content::LoadNotificationDetails; |
23 using content::NavigationController; | 32 using content::NavigationController; |
24 using content::RenderViewHost; | 33 using content::RenderViewHost; |
25 using content::ResourceRedirectDetails; | 34 using content::ResourceRedirectDetails; |
26 using content::WebContents; | 35 using content::WebContents; |
27 using content::WebContentsObserver; | 36 using content::WebContentsObserver; |
28 | 37 |
29 namespace extensions { | 38 namespace extensions { |
30 | 39 |
31 WebAuthFlow::WebAuthFlow( | 40 WebAuthFlow::WebAuthFlow( |
32 Delegate* delegate, | 41 Delegate* delegate, |
33 Profile* profile, | 42 Profile* profile, |
34 const GURL& provider_url, | 43 const GURL& provider_url, |
35 Mode mode, | 44 Mode mode) |
36 const gfx::Rect& initial_bounds, | |
37 chrome::HostDesktopType host_desktop_type) | |
38 : delegate_(delegate), | 45 : delegate_(delegate), |
| 46 closed_(false), |
39 profile_(profile), | 47 profile_(profile), |
40 provider_url_(provider_url), | 48 provider_url_(provider_url), |
41 mode_(mode), | 49 mode_(mode), |
42 initial_bounds_(initial_bounds), | 50 shell_window_loaded_(false) { |
43 host_desktop_type_(host_desktop_type), | |
44 popup_shown_(false), | |
45 contents_(NULL) { | |
46 } | 51 } |
47 | 52 |
48 WebAuthFlow::~WebAuthFlow() { | 53 WebAuthFlow::~WebAuthFlow() { |
| 54 DCHECK(closed_); |
| 55 } |
| 56 |
| 57 void WebAuthFlow::Start() { |
| 58 ShellWindowRegistry::Get(profile_)->AddObserver(this); |
| 59 |
| 60 std::string random_bytes; |
| 61 crypto::RandBytes(WriteInto(&random_bytes, 33), 32); |
| 62 std::string key; |
| 63 bool success = base::Base64Encode(random_bytes, &shell_window_key_); |
| 64 DCHECK(success); |
| 65 |
| 66 scoped_ptr<ListValue> args(new ListValue()); |
| 67 args->AppendString(shell_window_key_); |
| 68 args->AppendString(provider_url_.spec()); |
| 69 if (mode_ == WebAuthFlow::INTERACTIVE) |
| 70 args->AppendString("interactive"); |
| 71 else |
| 72 args->AppendString("silent"); |
| 73 |
| 74 scoped_ptr<Event> event(new Event("identityPrivate.onWebFlowRequest", |
| 75 args.Pass())); |
| 76 event->restrict_to_profile = profile_; |
| 77 ExtensionSystem* system = ExtensionSystem::Get(profile_); |
| 78 system->event_router()->AddLazyEventListener( |
| 79 "identityPrivate.onWebFlowRequest", extension_misc::kIdentityApiUiAppId); |
| 80 system->event_router()->DispatchEventToExtension( |
| 81 extension_misc::kIdentityApiUiAppId, event.Pass()); |
| 82 system->event_router()->RemoveLazyEventListener( |
| 83 "identityPrivate.onWebFlowRequest", extension_misc::kIdentityApiUiAppId); |
| 84 } |
| 85 |
| 86 void WebAuthFlow::Close() { |
| 87 CHECK(!closed_); |
| 88 closed_ = true; |
| 89 |
| 90 // Chances are good that we are currently inside an observer callback, so |
| 91 // detaching listeners now is dangerous. Use a task to unwind the stack |
| 92 // before cleaning up. |
| 93 MessageLoop::current()->PostTask(FROM_HERE, base::Bind( |
| 94 &WebAuthFlow::DeferredClose, base::Unretained(this))); |
| 95 } |
| 96 |
| 97 void WebAuthFlow::DeferredClose() { |
| 98 DCHECK(closed_); |
| 99 |
49 // Stop listening to notifications first since some of the code | 100 // Stop listening to notifications first since some of the code |
50 // below may generate notifications. | 101 // below may generate notifications. |
| 102 WebContentsObserver::Observe(NULL); |
51 registrar_.RemoveAll(); | 103 registrar_.RemoveAll(); |
52 WebContentsObserver::Observe(NULL); | 104 ShellWindowRegistry::Get(profile_)->RemoveObserver(this); |
53 | 105 |
54 if (contents_) { | 106 if (shell_window_ && shell_window_->web_contents()) |
55 // The popup owns the contents if it was displayed. | 107 shell_window_->web_contents()->Close(); |
56 if (popup_shown_) | 108 |
57 contents_->Close(); | 109 delegate_->OnAuthFlowClosed(); |
58 else | 110 } |
59 delete contents_; | 111 |
| 112 void WebAuthFlow::OnShellWindowAdded(ShellWindow* shell_window) { |
| 113 if (closed_) |
| 114 return; |
| 115 |
| 116 if (shell_window->window_key() == shell_window_key_ && |
| 117 shell_window->extension()->id() == extension_misc::kIdentityApiUiAppId) { |
| 118 shell_window_ = shell_window; |
| 119 WebContentsObserver::Observe(shell_window->web_contents()); |
| 120 |
| 121 registrar_.Add( |
| 122 this, |
| 123 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, |
| 124 content::NotificationService::AllBrowserContextsAndSources()); |
60 } | 125 } |
61 } | 126 } |
62 | 127 |
63 void WebAuthFlow::Start() { | 128 void WebAuthFlow::OnShellWindowRemoved(ShellWindow* shell_window) { |
64 contents_ = CreateWebContents(); | 129 if (closed_) |
65 WebContentsObserver::Observe(contents_); | 130 return; |
66 | 131 |
67 NavigationController* controller = &(contents_->GetController()); | 132 if (shell_window->window_key() == shell_window_key_ && |
68 | 133 shell_window->extension()->id() == extension_misc::kIdentityApiUiAppId) { |
69 // Register for appropriate notifications to intercept navigation to the | 134 shell_window_ = NULL; |
70 // redirect URLs. | 135 if (!closed_) |
71 registrar_.Add( | 136 delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED); |
72 this, | 137 } |
73 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, | |
74 content::Source<WebContents>(contents_)); | |
75 | |
76 controller->LoadURL( | |
77 provider_url_, | |
78 content::Referrer(), | |
79 content::PAGE_TRANSITION_AUTO_TOPLEVEL, | |
80 std::string()); | |
81 } | |
82 | |
83 WebContents* WebAuthFlow::CreateWebContents() { | |
84 return WebContents::Create(WebContents::CreateParams(profile_)); | |
85 } | |
86 | |
87 void WebAuthFlow::ShowAuthFlowPopup() { | |
88 Browser::CreateParams browser_params(Browser::TYPE_POPUP, profile_, | |
89 host_desktop_type_); | |
90 browser_params.initial_bounds = initial_bounds_; | |
91 Browser* browser = new Browser(browser_params); | |
92 chrome::NavigateParams params(browser, contents_); | |
93 params.disposition = CURRENT_TAB; | |
94 params.window_action = chrome::NavigateParams::SHOW_WINDOW; | |
95 chrome::Navigate(¶ms); | |
96 // Observe method and WebContentsObserver::* methods will be called | |
97 // for varous navigation events. That is where we check for redirect | |
98 // to the right URL. | |
99 popup_shown_ = true; | |
100 } | 138 } |
101 | 139 |
102 void WebAuthFlow::BeforeUrlLoaded(const GURL& url) { | 140 void WebAuthFlow::BeforeUrlLoaded(const GURL& url) { |
103 delegate_->OnAuthFlowURLChange(url); | 141 delegate_->OnAuthFlowURLChange(url); |
104 } | 142 } |
105 | 143 |
106 void WebAuthFlow::AfterUrlLoaded() { | 144 void WebAuthFlow::AfterUrlLoaded() { |
107 // Do nothing if a popup is already created. | 145 if (shell_window_loaded_ && mode_ == WebAuthFlow::SILENT) { |
108 if (popup_shown_) | |
109 return; | |
110 | |
111 // Report results directly if not in interactive mode. | |
112 if (mode_ != WebAuthFlow::INTERACTIVE) { | |
113 delegate_->OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED); | 146 delegate_->OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED); |
114 return; | 147 return; |
115 } | 148 } |
116 | |
117 // We are in interactive mode and window is not shown yet; show the window. | |
118 ShowAuthFlowPopup(); | |
119 } | 149 } |
120 | 150 |
121 void WebAuthFlow::Observe(int type, | 151 void WebAuthFlow::Observe(int type, |
122 const content::NotificationSource& source, | 152 const content::NotificationSource& source, |
123 const content::NotificationDetails& details) { | 153 const content::NotificationDetails& details) { |
| 154 if (closed_) |
| 155 return; |
| 156 |
124 switch (type) { | 157 switch (type) { |
| 158 case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: { |
| 159 RenderViewHost* render_view( |
| 160 content::Details<RenderViewHost>(details).ptr()); |
| 161 WebContents* web_contents = WebContents::FromRenderViewHost(render_view); |
| 162 |
| 163 if (web_contents && |
| 164 (web_contents->GetEmbedderWebContents() == |
| 165 WebContentsObserver::web_contents())) { |
| 166 // Switch from watching the shell window to the guest inside it. |
| 167 shell_window_loaded_ = true; |
| 168 registrar_.RemoveAll(); |
| 169 WebContentsObserver::Observe(web_contents); |
| 170 |
| 171 registrar_.Add( |
| 172 this, |
| 173 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, |
| 174 content::Source<WebContents>(web_contents)); |
| 175 } |
| 176 } |
| 177 break; |
125 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: { | 178 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: { |
126 ResourceRedirectDetails* redirect_details = | 179 ResourceRedirectDetails* redirect_details = |
127 content::Details<ResourceRedirectDetails>(details).ptr(); | 180 content::Details<ResourceRedirectDetails>(details).ptr(); |
128 if (redirect_details != NULL) | 181 if (redirect_details != NULL) |
129 BeforeUrlLoaded(redirect_details->new_url); | 182 BeforeUrlLoaded(redirect_details->new_url); |
130 } | 183 } |
131 break; | 184 break; |
132 default: | 185 default: |
133 NOTREACHED() << "Got a notification that we did not register for: " | 186 NOTREACHED() << "Got a notification that we did not register for: " |
134 << type; | 187 << type; |
135 break; | 188 break; |
136 } | 189 } |
137 } | 190 } |
138 | 191 |
139 void WebAuthFlow::ProvisionalChangeToMainFrameUrl( | 192 void WebAuthFlow::RenderViewGone(base::TerminationStatus status) { |
140 const GURL& url, | 193 if (closed_) |
| 194 return; |
| 195 |
| 196 delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED); |
| 197 } |
| 198 |
| 199 void WebAuthFlow::DidStartProvisionalLoadForFrame( |
| 200 int64 frame_id, |
| 201 int64 parent_frame_id, |
| 202 bool is_main_frame, |
| 203 const GURL& validated_url, |
| 204 bool is_error_page, |
| 205 bool is_iframe_srcdoc, |
141 RenderViewHost* render_view_host) { | 206 RenderViewHost* render_view_host) { |
142 BeforeUrlLoaded(url); | 207 if (closed_) |
| 208 return; |
| 209 |
| 210 if (is_main_frame) |
| 211 BeforeUrlLoaded(validated_url); |
| 212 } |
| 213 |
| 214 void WebAuthFlow::DidFailProvisionalLoad(int64 frame_id, |
| 215 bool is_main_frame, |
| 216 const GURL& validated_url, |
| 217 int error_code, |
| 218 const string16& error_description, |
| 219 RenderViewHost* render_view_host) { |
| 220 if (closed_) |
| 221 return; |
| 222 |
| 223 delegate_->OnAuthFlowFailure(LOAD_FAILED); |
| 224 } |
| 225 |
| 226 void WebAuthFlow::DidFailLoad(int64 frame_id, |
| 227 const GURL& validated_url, |
| 228 bool is_main_frame, |
| 229 int error_code, |
| 230 const string16& error_description, |
| 231 RenderViewHost* render_view_host) { |
| 232 if (closed_) |
| 233 return; |
| 234 |
| 235 delegate_->OnAuthFlowFailure(LOAD_FAILED); |
143 } | 236 } |
144 | 237 |
145 void WebAuthFlow::DidStopLoading(RenderViewHost* render_view_host) { | 238 void WebAuthFlow::DidStopLoading(RenderViewHost* render_view_host) { |
| 239 if (closed_) |
| 240 return; |
| 241 |
146 AfterUrlLoaded(); | 242 AfterUrlLoaded(); |
147 } | 243 } |
148 | 244 |
149 void WebAuthFlow::WebContentsDestroyed(WebContents* web_contents) { | |
150 contents_ = NULL; | |
151 delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED); | |
152 } | |
153 | |
154 } // namespace extensions | 245 } // namespace extensions |
OLD | NEW |