Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(19)

Side by Side Diff: chrome/browser/extensions/api/identity/web_auth_flow.cc

Issue 15897006: Identity API: switch WebAuthFlow dialog to component app (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase to ToT Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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"
8 #include "base/message_loop.h" 9 #include "base/message_loop.h"
10 #include "base/string_util.h"
9 #include "base/utf_string_conversions.h" 11 #include "base/utf_string_conversions.h"
12 #include "chrome/browser/extensions/component_loader.h"
13 #include "chrome/browser/extensions/event_router.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/extensions/extension_system.h"
16 #include "chrome/browser/extensions/extension_system.h"
10 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/ui/browser.h" 18 #include "chrome/browser/ui/extensions/shell_window.h"
12 #include "chrome/browser/ui/browser_navigator.h" 19 #include "chrome/common/extensions/extension_constants.h"
13 #include "content/public/browser/load_notification_details.h" 20 #include "content/public/browser/navigation_details.h"
14 #include "content/public/browser/navigation_controller.h"
15 #include "content/public/browser/navigation_entry.h" 21 #include "content/public/browser/navigation_entry.h"
16 #include "content/public/browser/notification_details.h" 22 #include "content/public/browser/notification_details.h"
17 #include "content/public/browser/notification_source.h" 23 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/notification_types.h" 24 #include "content/public/browser/notification_types.h"
25 #include "content/public/browser/render_view_host.h"
19 #include "content/public/browser/resource_request_details.h" 26 #include "content/public/browser/resource_request_details.h"
20 #include "content/public/browser/web_contents.h" 27 #include "content/public/browser/web_contents.h"
21 #include "content/public/common/page_transition_types.h" 28 #include "crypto/random.h"
22 #include "googleurl/src/gurl.h" 29 #include "googleurl/src/gurl.h"
23 #include "ui/base/window_open_disposition.h" 30 #include "grit/browser_resources.h"
24 31
25 using content::LoadNotificationDetails;
26 using content::NavigationController;
27 using content::RenderViewHost; 32 using content::RenderViewHost;
28 using content::ResourceRedirectDetails; 33 using content::ResourceRedirectDetails;
29 using content::WebContents; 34 using content::WebContents;
30 using content::WebContentsObserver; 35 using content::WebContentsObserver;
31 36
32 namespace extensions { 37 namespace extensions {
33 38
34 WebAuthFlow::WebAuthFlow( 39 WebAuthFlow::WebAuthFlow(
35 Delegate* delegate, 40 Delegate* delegate,
36 Profile* profile, 41 Profile* profile,
37 const GURL& provider_url, 42 const GURL& provider_url,
38 Mode mode, 43 Mode mode)
39 const gfx::Rect& initial_bounds,
40 chrome::HostDesktopType host_desktop_type)
41 : delegate_(delegate), 44 : delegate_(delegate),
42 profile_(profile), 45 profile_(profile),
43 provider_url_(provider_url), 46 provider_url_(provider_url),
44 mode_(mode), 47 mode_(mode),
45 initial_bounds_(initial_bounds), 48 embedded_window_created_(false) {
46 host_desktop_type_(host_desktop_type),
47 popup_shown_(false),
48 contents_(NULL) {
49 } 49 }
50 50
51 WebAuthFlow::~WebAuthFlow() { 51 WebAuthFlow::~WebAuthFlow() {
52 DCHECK(delegate_ == NULL); 52 DCHECK(delegate_ == NULL);
53 53
54 // Stop listening to notifications first since some of the code 54 // Stop listening to notifications first since some of the code
55 // below may generate notifications. 55 // below may generate notifications.
56 registrar_.RemoveAll(); 56 registrar_.RemoveAll();
57 WebContentsObserver::Observe(NULL); 57 WebContentsObserver::Observe(NULL);
58 58
59 if (contents_) { 59 if (!shell_window_key_.empty()) {
60 // The popup owns the contents if it was displayed. 60 ShellWindowRegistry::Get(profile_)->RemoveObserver(this);
61 if (popup_shown_) 61
62 contents_->Close(); 62 if (shell_window_ && shell_window_->web_contents())
63 else 63 shell_window_->web_contents()->Close();
64 delete contents_;
65 } 64 }
66 } 65 }
67 66
68 void WebAuthFlow::Start() { 67 void WebAuthFlow::Start() {
69 contents_ = CreateWebContents(); 68 ShellWindowRegistry::Get(profile_)->AddObserver(this);
70 WebContentsObserver::Observe(contents_);
71 69
72 NavigationController* controller = &(contents_->GetController()); 70 // Attach a random ID string to the window so we can recoginize it
71 // in OnShellWindowAdded.
72 std::string random_bytes;
73 crypto::RandBytes(WriteInto(&random_bytes, 33), 32);
74 std::string key;
75 bool success = base::Base64Encode(random_bytes, &shell_window_key_);
76 DCHECK(success);
73 77
74 // Register for appropriate notifications to intercept navigation to the 78 // identityPrivate.onWebFlowRequest(shell_window_key, provider_url_, mode_)
75 // redirect URLs. 79 scoped_ptr<ListValue> args(new ListValue());
76 registrar_.Add( 80 args->AppendString(shell_window_key_);
77 this, 81 args->AppendString(provider_url_.spec());
78 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, 82 if (mode_ == WebAuthFlow::INTERACTIVE)
79 content::Source<WebContents>(contents_)); 83 args->AppendString("interactive");
80 registrar_.Add( 84 else
81 this, 85 args->AppendString("silent");
82 content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED,
83 content::Source<WebContents>(contents_));
84 86
85 controller->LoadURL( 87 scoped_ptr<Event> event(
86 provider_url_, 88 new Event("identityPrivate.onWebFlowRequest", args.Pass()));
87 content::Referrer(), 89 event->restrict_to_profile = profile_;
88 content::PAGE_TRANSITION_AUTO_TOPLEVEL, 90 ExtensionSystem* system = ExtensionSystem::Get(profile_);
89 std::string()); 91
92 extensions::ComponentLoader* component_loader =
93 system->extension_service()->component_loader();
94 if (!component_loader->Exists(extension_misc::kIdentityApiUiAppId)) {
95 component_loader->Add(
96 IDR_IDENTITY_API_SCOPE_APPROVAL_MANIFEST,
97 base::FilePath(FILE_PATH_LITERAL("identity_scope_approval_dialog")));
98 }
99
100 system->event_router()->AddLazyEventListener(
101 "identityPrivate.onWebFlowRequest", extension_misc::kIdentityApiUiAppId);
102 system->event_router()->DispatchEventToExtension(
103 extension_misc::kIdentityApiUiAppId, event.Pass());
104 system->event_router()->RemoveLazyEventListener(
105 "identityPrivate.onWebFlowRequest", extension_misc::kIdentityApiUiAppId);
90 } 106 }
91 107
92 void WebAuthFlow::DetachDelegateAndDelete() { 108 void WebAuthFlow::DetachDelegateAndDelete() {
93 delegate_ = NULL; 109 delegate_ = NULL;
94 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 110 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
95 } 111 }
96 112
97 WebContents* WebAuthFlow::CreateWebContents() { 113 void WebAuthFlow::OnShellWindowAdded(ShellWindow* shell_window) {
98 return WebContents::Create(WebContents::CreateParams(profile_)); 114 if (shell_window->window_key() == shell_window_key_ &&
115 shell_window->extension()->id() == extension_misc::kIdentityApiUiAppId) {
116 shell_window_ = shell_window;
117 WebContentsObserver::Observe(shell_window->web_contents());
118
119 registrar_.Add(
120 this,
121 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
122 content::NotificationService::AllBrowserContextsAndSources());
123 }
99 } 124 }
100 125
101 void WebAuthFlow::ShowAuthFlowPopup() { 126 void WebAuthFlow::OnShellWindowRemoved(ShellWindow* shell_window) {
102 Browser::CreateParams browser_params(Browser::TYPE_POPUP, profile_, 127 if (shell_window->window_key() == shell_window_key_ &&
103 host_desktop_type_); 128 shell_window->extension()->id() == extension_misc::kIdentityApiUiAppId) {
104 browser_params.initial_bounds = initial_bounds_; 129 shell_window_ = NULL;
105 Browser* browser = new Browser(browser_params); 130 registrar_.RemoveAll();
106 chrome::NavigateParams params(browser, contents_); 131
107 params.disposition = CURRENT_TAB; 132 if (delegate_)
108 params.window_action = chrome::NavigateParams::SHOW_WINDOW; 133 delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED);
109 chrome::Navigate(&params); 134 }
110 // Observe method and WebContentsObserver::* methods will be called
111 // for varous navigation events. That is where we check for redirect
112 // to the right URL.
113 popup_shown_ = true;
114 } 135 }
115 136
116 void WebAuthFlow::BeforeUrlLoaded(const GURL& url) { 137 void WebAuthFlow::BeforeUrlLoaded(const GURL& url) {
117 if (delegate_) 138 if (delegate_ && embedded_window_created_)
118 delegate_->OnAuthFlowURLChange(url); 139 delegate_->OnAuthFlowURLChange(url);
119 } 140 }
120 141
121 void WebAuthFlow::AfterUrlLoaded() { 142 void WebAuthFlow::AfterUrlLoaded() {
122 // Do nothing if a popup is already created. 143 if (delegate_ && embedded_window_created_ && mode_ == WebAuthFlow::SILENT)
123 if (popup_shown_) 144 delegate_->OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED);
124 return;
125
126 // Report results directly if not in interactive mode.
127 if (mode_ != WebAuthFlow::INTERACTIVE) {
128 if (delegate_)
129 delegate_->OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED);
130 return;
131 }
132
133 // We are in interactive mode and window is not shown yet; show the window.
134 ShowAuthFlowPopup();
135 } 145 }
136 146
137 void WebAuthFlow::Observe(int type, 147 void WebAuthFlow::Observe(int type,
138 const content::NotificationSource& source, 148 const content::NotificationSource& source,
139 const content::NotificationDetails& details) { 149 const content::NotificationDetails& details) {
140 switch (type) { 150 DCHECK(shell_window_);
141 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: { 151
142 ResourceRedirectDetails* redirect_details = 152 if (!delegate_)
143 content::Details<ResourceRedirectDetails>(details).ptr(); 153 return;
144 if (redirect_details != NULL) 154
145 BeforeUrlLoaded(redirect_details->new_url); 155 if (!embedded_window_created_) {
156 DCHECK(type == content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED);
157
158 RenderViewHost* render_view(
159 content::Details<RenderViewHost>(details).ptr());
160 WebContents* web_contents = WebContents::FromRenderViewHost(render_view);
161
162 if (web_contents &&
163 (web_contents->GetEmbedderWebContents() ==
164 WebContentsObserver::web_contents())) {
165 // Switch from watching the shell window to the guest inside it.
166 embedded_window_created_ = true;
167 WebContentsObserver::Observe(web_contents);
168
169 registrar_.RemoveAll();
170 registrar_.Add(this,
171 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
172 content::Source<WebContents>(web_contents));
173 registrar_.Add(this,
174 content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED,
175 content::Source<WebContents>(web_contents));
146 } 176 }
147 break; 177 } else {
148 case content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED: { 178 // embedded_window_created_
149 std::pair<content::NavigationEntry*, bool>* title = 179 switch (type) {
150 content::Details< 180 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
151 std::pair<content::NavigationEntry*, bool> >(details).ptr(); 181 ResourceRedirectDetails* redirect_details =
182 content::Details<ResourceRedirectDetails>(details).ptr();
183 if (redirect_details != NULL)
184 BeforeUrlLoaded(redirect_details->new_url);
185 break;
186 }
187 case content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED: {
188 std::pair<content::NavigationEntry*, bool>* title =
189 content::Details<std::pair<content::NavigationEntry*, bool> >(
190 details).ptr();
152 191
153 if (title->first) 192 if (title->first) {
154 delegate_->OnAuthFlowTitleChange(UTF16ToUTF8(title->first->GetTitle())); 193 delegate_->OnAuthFlowTitleChange(
194 UTF16ToUTF8(title->first->GetTitle()));
195 }
196 break;
197 }
198 default:
199 NOTREACHED()
200 << "Got a notification that we did not register for: " << type;
201 break;
155 } 202 }
156 break;
157 default:
158 NOTREACHED() << "Got a notification that we did not register for: "
159 << type;
160 break;
161 } 203 }
162 } 204 }
163 205
164 void WebAuthFlow::ProvisionalChangeToMainFrameUrl( 206 void WebAuthFlow::RenderViewGone(base::TerminationStatus status) {
165 const GURL& url, 207 if (delegate_)
208 delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED);
209 }
210
211 void WebAuthFlow::DidStartProvisionalLoadForFrame(
212 int64 frame_id,
213 int64 parent_frame_id,
214 bool is_main_frame,
215 const GURL& validated_url,
216 bool is_error_page,
217 bool is_iframe_srcdoc,
166 RenderViewHost* render_view_host) { 218 RenderViewHost* render_view_host) {
167 BeforeUrlLoaded(url); 219 if (is_main_frame)
220 BeforeUrlLoaded(validated_url);
221 }
222
223 void WebAuthFlow::DidFailProvisionalLoad(int64 frame_id,
224 bool is_main_frame,
225 const GURL& validated_url,
226 int error_code,
227 const string16& error_description,
228 RenderViewHost* render_view_host) {
229 if (delegate_)
230 delegate_->OnAuthFlowFailure(LOAD_FAILED);
168 } 231 }
169 232
170 void WebAuthFlow::DidStopLoading(RenderViewHost* render_view_host) { 233 void WebAuthFlow::DidStopLoading(RenderViewHost* render_view_host) {
171 AfterUrlLoaded(); 234 AfterUrlLoaded();
172 } 235 }
173 236
174 void WebAuthFlow::WebContentsDestroyed(WebContents* web_contents) { 237 void WebAuthFlow::DidNavigateMainFrame(
175 contents_ = NULL; 238 const content::LoadCommittedDetails& details,
176 if (delegate_) 239 const content::FrameNavigateParams& params) {
177 delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED); 240 if (delegate_ && details.http_status_code >= 400)
241 delegate_->OnAuthFlowFailure(LOAD_FAILED);
178 } 242 }
179 243
180 } // namespace extensions 244 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698