1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/devtools/device/webrtc/devtools_bridge_client.h"
7 #include "chrome/browser/chrome_notification_types.h"
8 #include "chrome/browser/local_discovery/gcd_api_flow.h"
9 #include "chrome/browser/signin/profile_identity_provider.h"
10 #include "chrome/common/url_constants.h"
11 #include "content/public/browser/notification_observer.h"
12 #include "content/public/browser/notification_registrar.h"
13 #include "content/public/browser/notification_source.h"
14 #include "content/public/browser/web_contents.h"
15 #include "content/public/browser/web_contents_observer.h"
16 #include "content/public/browser/web_contents_user_data.h"
17 #include "ui/base/page_transition_types.h"
19 using content::BrowserThread
;
20 using content::WebContents
;
24 const char kBackgroundWorkerURL
[] =
25 "chrome://webrtc-device-provider/background_worker.html";
26 const char kSerial
[] = "webrtc";
27 const char kPseudoDeviceName
[] = "Remote browsers";
28 const char kDeviceIdPrefix
[] = "device-id:";
30 class BackgroundWorkerUserData
31 : public content::WebContentsUserData
<BackgroundWorkerUserData
> {
33 DevToolsBridgeClient
* client() const { return client_
; }
34 void SetClient(DevToolsBridgeClient
* client
) { client_
= client
; }
37 friend WebContentsUserData
<BackgroundWorkerUserData
>;
39 explicit BackgroundWorkerUserData(WebContents
* contents
) : client_(nullptr) {}
41 DevToolsBridgeClient
* client_
;
46 DEFINE_WEB_CONTENTS_USER_DATA_KEY(BackgroundWorkerUserData
);
48 // DevToolsBridgeClient --------------------------------------------------------
51 base::WeakPtr
<DevToolsBridgeClient
> DevToolsBridgeClient::Create(
53 SigninManagerBase
* signin_manager
,
54 ProfileOAuth2TokenService
* token_service
) {
55 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
57 new DevToolsBridgeClient(profile
, signin_manager
, token_service
);
58 return instance
->weak_factory_
.GetWeakPtr();
61 DevToolsBridgeClient::DevToolsBridgeClient(
63 SigninManagerBase
* signin_manager
,
64 ProfileOAuth2TokenService
* token_service
)
65 : WebContentsObserver(),
67 identity_provider_(signin_manager
, token_service
, nullptr),
68 worker_is_loaded_(false),
70 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
72 identity_provider_
.AddObserver(this);
73 registrar_
.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED
,
74 content::Source
<Profile
>(profile_
));
76 if (IsAuthenticated())
77 CreateBackgroundWorker();
80 DevToolsBridgeClient::~DevToolsBridgeClient() {
81 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
83 identity_provider_
.RemoveObserver(this);
86 void DevToolsBridgeClient::DeleteSelf() {
87 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
91 void DevToolsBridgeClient::UpdateBrowserList() {
92 if (!IsAuthenticated() || browser_list_request_
.get())
94 browser_list_request_
= CreateGCDApiFlow();
95 browser_list_request_
->Start(
96 make_scoped_ptr(new DevToolsBridgeInstancesRequest(this)));
99 void DevToolsBridgeClient::StartSessionIfNeeded(
100 const std::string
& socket_name
) {
101 if (!background_worker_
.get() || !background_worker_
->GetWebUI() ||
102 !worker_is_loaded_
) {
106 const size_t kPrefixLength
= sizeof(kDeviceIdPrefix
) - 1;
107 if (socket_name
.substr(0, kPrefixLength
) != kDeviceIdPrefix
)
110 std::string browser_id
= socket_name
.substr(kPrefixLength
);
111 background_worker_
->GetWebUI()->CallJavascriptFunction(
112 "WebRTCDeviceProvider.instance.startSessionIfNeeded",
113 base::StringValue(browser_id
));
117 DevToolsBridgeClient
* DevToolsBridgeClient::FromWebContents(
118 WebContents
* web_contents
) {
119 auto user_data
= BackgroundWorkerUserData::FromWebContents(web_contents
);
120 return user_data
? user_data
->client() : nullptr;
123 void DevToolsBridgeClient::RegisterMessageHandlers(content::WebUI
* web_ui
) {
124 web_ui
->RegisterMessageCallback(
125 "sendCommand", base::Bind(&DevToolsBridgeClient::HandleSendCommand
,
126 base::Unretained(this)));
129 bool DevToolsBridgeClient::IsAuthenticated() {
130 return !identity_provider_
.GetActiveAccountId().empty();
133 void DevToolsBridgeClient::HandleSendCommand(const base::ListValue
* args
) {
134 if (args
->GetSize() != 1)
137 const base::DictionaryValue
* command_value
;
138 if (!args
->GetDictionary(0, &command_value
))
141 send_command_request_
= CreateGCDApiFlow();
142 send_command_request_
->Start(
143 make_scoped_ptr(new SendCommandRequest(command_value
, this)));
146 scoped_ptr
<local_discovery::GCDApiFlow
>
147 DevToolsBridgeClient::CreateGCDApiFlow() {
148 DCHECK(IsAuthenticated());
149 return local_discovery::GCDApiFlow::Create(
150 profile_
->GetRequestContext(), identity_provider_
.GetTokenService(),
151 identity_provider_
.GetActiveAccountId());
155 DevToolsBridgeClient::SerialList
DevToolsBridgeClient::GetDevices(
156 base::WeakPtr
<DevToolsBridgeClient
> weak_ptr
) {
158 if (auto* ptr
= weak_ptr
.get()) {
159 if (ptr
->background_worker_
.get())
160 result
.push_back(kSerial
);
162 ptr
->UpdateBrowserList();
168 DevToolsBridgeClient::DeviceInfo
DevToolsBridgeClient::GetDeviceInfo(
169 base::WeakPtr
<DevToolsBridgeClient
> weak_self
,
170 const std::string
& serial
) {
172 if (auto* self
= weak_self
.get()) {
173 result
.connected
= !!self
->background_worker_
.get();
174 result
.model
= kPseudoDeviceName
;
175 result
.browser_info
= self
->browsers_
;
180 void DevToolsBridgeClient::CreateBackgroundWorker() {
181 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
183 background_worker_
.reset(
184 WebContents::Create(WebContents::CreateParams(profile_
)));
186 BackgroundWorkerUserData::CreateForWebContents(background_worker_
.get());
187 BackgroundWorkerUserData::FromWebContents(background_worker_
.get())
189 WebContentsObserver::Observe(background_worker_
.get());
191 GURL
url(kBackgroundWorkerURL
);
192 DCHECK_EQ(chrome::kChromeUIWebRTCDeviceProviderHost
, url
.host());
194 background_worker_
->GetController().LoadURL(url
, content::Referrer(),
195 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
199 void DevToolsBridgeClient::DocumentOnLoadCompletedInMainFrame() {
200 worker_is_loaded_
= true;
203 void DevToolsBridgeClient::Observe(
205 const content::NotificationSource
& source
,
206 const content::NotificationDetails
& details
) {
207 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
208 DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED
, type
);
213 void DevToolsBridgeClient::OnActiveAccountLogin() {
214 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
215 CreateBackgroundWorker();
218 void DevToolsBridgeClient::OnActiveAccountLogout() {
219 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
220 background_worker_
.reset();
221 browser_list_request_
.reset();
222 send_command_request_
.reset();
223 BrowserInfoList().swap(browsers_
);
224 worker_is_loaded_
= false;
227 void DevToolsBridgeClient::OnCommandSucceeded(
228 const base::DictionaryValue
& response
) {
229 if (background_worker_
.get() && background_worker_
->GetWebUI()) {
230 background_worker_
->GetWebUI()->CallJavascriptFunction(
231 "WebRTCDeviceProvider.instance.handleCommandSuccess", response
);
233 send_command_request_
.reset();
236 void DevToolsBridgeClient::OnCommandFailed() {
237 if (background_worker_
.get() && background_worker_
->GetWebUI()) {
238 background_worker_
->GetWebUI()->CallJavascriptFunction(
239 "WebRTCDeviceProvider.instance.handleCommandFailure");
241 send_command_request_
.reset();
244 void DevToolsBridgeClient::OnDevToolsBridgeInstancesRequestSucceeded(
245 const DevToolsBridgeInstancesRequest::InstanceList
& instances
) {
246 BrowserInfoList browsers
;
247 for (const auto& instance
: instances
) {
249 browser
.type
= BrowserInfo::kTypeChrome
;
250 browser
.display_name
= instance
.display_name
;
251 browser
.socket_name
= kDeviceIdPrefix
+ instance
.id
;
252 browsers
.push_back(browser
);
254 browsers_
.swap(browsers
);
256 browser_list_request_
.reset();
258 OnBrowserListUpdatedForTests();
261 void DevToolsBridgeClient::OnDevToolsBridgeInstancesRequestFailed() {
262 // We keep the list of remote browsers even if the request failed.
263 browser_list_request_
.reset();