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 "base/callback.h"
8 #include "chrome/browser/chrome_notification_types.h"
9 #include "chrome/browser/local_discovery/gcd_api_flow.h"
10 #include "chrome/common/url_constants.h"
11 #include "components/signin/core/browser/profile_identity_provider.h"
12 #include "content/public/browser/notification_observer.h"
13 #include "content/public/browser/notification_registrar.h"
14 #include "content/public/browser/notification_source.h"
15 #include "content/public/browser/web_contents.h"
16 #include "content/public/browser/web_contents_observer.h"
17 #include "content/public/browser/web_contents_user_data.h"
18 #include "ui/base/page_transition_types.h"
20 using content::BrowserThread
;
21 using content::WebContents
;
25 const char kBackgroundWorkerURL
[] =
26 "chrome://webrtc-device-provider/background_worker.html";
27 const char kSerial
[] = "webrtc";
28 const char kPseudoDeviceName
[] = "Remote browsers";
29 const char kDeviceIdPrefix
[] = "device-id:";
31 class BackgroundWorkerUserData
32 : public content::WebContentsUserData
<BackgroundWorkerUserData
> {
34 DevToolsBridgeClient
* client() const { return client_
; }
35 void SetClient(DevToolsBridgeClient
* client
) { client_
= client
; }
38 friend WebContentsUserData
<BackgroundWorkerUserData
>;
40 explicit BackgroundWorkerUserData(WebContents
* contents
) : client_(nullptr) {}
42 DevToolsBridgeClient
* client_
;
47 DEFINE_WEB_CONTENTS_USER_DATA_KEY(BackgroundWorkerUserData
);
49 // DevToolsBridgeClient --------------------------------------------------------
52 base::WeakPtr
<DevToolsBridgeClient
> DevToolsBridgeClient::Create(
54 SigninManagerBase
* signin_manager
,
55 ProfileOAuth2TokenService
* token_service
) {
56 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
58 new DevToolsBridgeClient(profile
, signin_manager
, token_service
);
59 return instance
->weak_factory_
.GetWeakPtr();
62 DevToolsBridgeClient::DevToolsBridgeClient(
64 SigninManagerBase
* signin_manager
,
65 ProfileOAuth2TokenService
* token_service
)
66 : WebContentsObserver(),
68 identity_provider_(signin_manager
, token_service
, base::Closure()),
69 worker_is_loaded_(false),
71 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
73 identity_provider_
.AddObserver(this);
74 registrar_
.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED
,
75 content::Source
<Profile
>(profile_
));
77 if (IsAuthenticated())
78 CreateBackgroundWorker();
81 DevToolsBridgeClient::~DevToolsBridgeClient() {
82 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
84 identity_provider_
.RemoveObserver(this);
87 void DevToolsBridgeClient::DeleteSelf() {
88 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
92 void DevToolsBridgeClient::UpdateBrowserList() {
93 if (!IsAuthenticated() || browser_list_request_
.get())
95 browser_list_request_
= CreateGCDApiFlow();
96 browser_list_request_
->Start(
97 make_scoped_ptr(new DevToolsBridgeInstancesRequest(this)));
100 void DevToolsBridgeClient::StartSessionIfNeeded(
101 const std::string
& socket_name
) {
102 if (!background_worker_
.get() || !background_worker_
->GetWebUI() ||
103 !worker_is_loaded_
) {
107 const size_t kPrefixLength
= sizeof(kDeviceIdPrefix
) - 1;
108 if (socket_name
.substr(0, kPrefixLength
) != kDeviceIdPrefix
)
111 std::string browser_id
= socket_name
.substr(kPrefixLength
);
112 background_worker_
->GetWebUI()->CallJavascriptFunction(
113 "WebRTCDeviceProvider.instance.startSessionIfNeeded",
114 base::StringValue(browser_id
));
118 DevToolsBridgeClient
* DevToolsBridgeClient::FromWebContents(
119 WebContents
* web_contents
) {
120 auto user_data
= BackgroundWorkerUserData::FromWebContents(web_contents
);
121 return user_data
? user_data
->client() : nullptr;
124 void DevToolsBridgeClient::RegisterMessageHandlers(content::WebUI
* web_ui
) {
125 web_ui
->RegisterMessageCallback(
126 "sendCommand", base::Bind(&DevToolsBridgeClient::HandleSendCommand
,
127 base::Unretained(this)));
130 bool DevToolsBridgeClient::IsAuthenticated() {
131 return !identity_provider_
.GetActiveAccountId().empty();
134 void DevToolsBridgeClient::HandleSendCommand(const base::ListValue
* args
) {
135 if (args
->GetSize() != 1)
138 const base::DictionaryValue
* command_value
;
139 if (!args
->GetDictionary(0, &command_value
))
142 send_command_request_
= CreateGCDApiFlow();
143 send_command_request_
->Start(
144 make_scoped_ptr(new SendCommandRequest(command_value
, this)));
147 scoped_ptr
<local_discovery::GCDApiFlow
>
148 DevToolsBridgeClient::CreateGCDApiFlow() {
149 DCHECK(IsAuthenticated());
150 return local_discovery::GCDApiFlow::Create(
151 profile_
->GetRequestContext(), identity_provider_
.GetTokenService(),
152 identity_provider_
.GetActiveAccountId());
156 DevToolsBridgeClient::SerialList
DevToolsBridgeClient::GetDevices(
157 base::WeakPtr
<DevToolsBridgeClient
> weak_ptr
) {
159 if (auto* ptr
= weak_ptr
.get()) {
160 if (ptr
->background_worker_
.get())
161 result
.push_back(kSerial
);
163 ptr
->UpdateBrowserList();
169 DevToolsBridgeClient::DeviceInfo
DevToolsBridgeClient::GetDeviceInfo(
170 base::WeakPtr
<DevToolsBridgeClient
> weak_self
,
171 const std::string
& serial
) {
173 if (auto* self
= weak_self
.get()) {
174 result
.connected
= !!self
->background_worker_
.get();
175 result
.model
= kPseudoDeviceName
;
176 result
.browser_info
= self
->browsers_
;
181 void DevToolsBridgeClient::CreateBackgroundWorker() {
182 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
184 background_worker_
.reset(
185 WebContents::Create(WebContents::CreateParams(profile_
)));
187 BackgroundWorkerUserData::CreateForWebContents(background_worker_
.get());
188 BackgroundWorkerUserData::FromWebContents(background_worker_
.get())
190 WebContentsObserver::Observe(background_worker_
.get());
192 GURL
url(kBackgroundWorkerURL
);
193 DCHECK_EQ(chrome::kChromeUIWebRTCDeviceProviderHost
, url
.host());
195 background_worker_
->GetController().LoadURL(url
, content::Referrer(),
196 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
200 void DevToolsBridgeClient::DocumentOnLoadCompletedInMainFrame() {
201 worker_is_loaded_
= true;
204 void DevToolsBridgeClient::Observe(
206 const content::NotificationSource
& source
,
207 const content::NotificationDetails
& details
) {
208 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
209 DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED
, type
);
214 void DevToolsBridgeClient::OnActiveAccountLogin() {
215 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
216 CreateBackgroundWorker();
219 void DevToolsBridgeClient::OnActiveAccountLogout() {
220 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
221 background_worker_
.reset();
222 browser_list_request_
.reset();
223 send_command_request_
.reset();
224 BrowserInfoList().swap(browsers_
);
225 worker_is_loaded_
= false;
228 void DevToolsBridgeClient::OnCommandSucceeded(
229 const base::DictionaryValue
& response
) {
230 if (background_worker_
.get() && background_worker_
->GetWebUI()) {
231 background_worker_
->GetWebUI()->CallJavascriptFunction(
232 "WebRTCDeviceProvider.instance.handleCommandSuccess", response
);
234 send_command_request_
.reset();
237 void DevToolsBridgeClient::OnCommandFailed() {
238 if (background_worker_
.get() && background_worker_
->GetWebUI()) {
239 background_worker_
->GetWebUI()->CallJavascriptFunction(
240 "WebRTCDeviceProvider.instance.handleCommandFailure");
242 send_command_request_
.reset();
245 void DevToolsBridgeClient::OnDevToolsBridgeInstancesRequestSucceeded(
246 const DevToolsBridgeInstancesRequest::InstanceList
& instances
) {
247 BrowserInfoList browsers
;
248 for (const auto& instance
: instances
) {
250 browser
.type
= BrowserInfo::kTypeChrome
;
251 browser
.display_name
= instance
.display_name
;
252 browser
.socket_name
= kDeviceIdPrefix
+ instance
.id
;
253 browsers
.push_back(browser
);
255 browsers_
.swap(browsers
);
257 browser_list_request_
.reset();
259 OnBrowserListUpdatedForTests();
262 void DevToolsBridgeClient::OnDevToolsBridgeInstancesRequestFailed() {
263 // We keep the list of remote browsers even if the request failed.
264 browser_list_request_
.reset();