blob: 14bbde2341eae335656d87c44ac4272ae7a4dd31 [file] [log] [blame]
liaoyuke8b8cfafb2015-06-29 20:41:201// Copyright 2015 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.
4
5#include "remoting/test/app_remoting_connection_helper.h"
6
7#include "base/json/json_reader.h"
8#include "base/logging.h"
9#include "base/run_loop.h"
10#include "base/thread_task_runner_handle.h"
11#include "base/timer/timer.h"
12#include "base/values.h"
13#include "remoting/protocol/host_stub.h"
14#include "remoting/test/app_remoting_test_driver_environment.h"
15#include "remoting/test/remote_application_details.h"
16#include "remoting/test/test_chromoting_client.h"
17
18namespace {
19const int kDefaultDPI = 96;
20const int kDefaultWidth = 1024;
21const int kDefaultHeight = 768;
22
23const char kHostProcessWindowTitle[] = "Host Process";
24} // namespace
25
26namespace remoting {
27namespace test {
28
29AppRemotingConnectionHelper::AppRemotingConnectionHelper(
30 const RemoteApplicationDetails& application_details)
31 : application_details_(application_details),
32 connection_is_ready_for_tests_(false),
33 timer_(new base::Timer(true, false)) {
34}
35
36AppRemotingConnectionHelper::~AppRemotingConnectionHelper() {
37 // |client_| destroys some of its members via DeleteSoon on the message loop's
38 // TaskRunner so we need to run the loop until it has no more work to do.
39 client_->RemoveRemoteConnectionObserver(this);
40 client_.reset();
41
42 base::RunLoop().RunUntilIdle();
43}
44
liaoyukea3153c12015-07-08 01:31:2045void AppRemotingConnectionHelper::Initialize(
46 scoped_ptr<TestChromotingClient> test_chromoting_client) {
47 client_ = test_chromoting_client.Pass();
liaoyuke8b8cfafb2015-06-29 20:41:2048 client_->AddRemoteConnectionObserver(this);
49}
50
51void AppRemotingConnectionHelper::SetHostMessageReceivedCallback(
52 HostMessageReceivedCallback host_message_received_callback) {
53 host_message_received_callback_ = host_message_received_callback;
54}
55
56void AppRemotingConnectionHelper::ResetHostMessageReceivedCallback() {
57 host_message_received_callback_.Reset();
58}
59
60bool AppRemotingConnectionHelper::StartConnection() {
61 DCHECK(thread_checker_.CalledOnValidThread());
liaoyukea3153c12015-07-08 01:31:2062 DCHECK(client_);
liaoyuke8b8cfafb2015-06-29 20:41:2063
64 RemoteHostInfo remote_host_info;
65 remoting::test::AppRemotingSharedData->GetRemoteHostInfoForApplicationId(
66 application_details_.application_id, &remote_host_info);
67
68 if (!remote_host_info.IsReadyForConnection()) {
69 return false;
70 }
joedow751a18f32015-07-02 01:39:0971 remoting::test::AppRemotingSharedData->AddHostToReleaseList(
72 application_details_.application_id, remote_host_info.host_id);
liaoyuke8b8cfafb2015-06-29 20:41:2073
74 DCHECK(!run_loop_ || !run_loop_->running());
75 run_loop_.reset(new base::RunLoop());
76
77 // We will wait up to 30 seconds to complete the remote connection and for the
78 // main application window to become visible.
79 DCHECK(!timer_->IsRunning());
80 timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(30),
81 run_loop_->QuitClosure());
82
83 client_->StartConnection(AppRemotingSharedData->user_name(),
84 AppRemotingSharedData->access_token(),
85 remote_host_info);
86
87 run_loop_->Run();
88 timer_->Stop();
89
90 if (connection_is_ready_for_tests_) {
91 return true;
92 } else {
93 client_->EndConnection();
94 return false;
95 }
96}
97
98protocol::ClipboardStub* AppRemotingConnectionHelper::clipboard_forwarder() {
99 return client_->clipboard_forwarder();
100}
101
102protocol::HostStub* AppRemotingConnectionHelper::host_stub() {
103 return client_->host_stub();
104}
105
106protocol::InputStub* AppRemotingConnectionHelper::input_stub() {
107 return client_->input_stub();
108}
109
110void AppRemotingConnectionHelper::ConnectionStateChanged(
111 protocol::ConnectionToHost::State state,
112 protocol::ErrorCode error_code) {
113 DCHECK(thread_checker_.CalledOnValidThread());
114
115 // If the connection is closed or failed then mark the connection as closed
116 // and quit the current RunLoop if it exists.
117 if (state == protocol::ConnectionToHost::CLOSED ||
118 state == protocol::ConnectionToHost::FAILED ||
119 error_code != protocol::OK) {
120 connection_is_ready_for_tests_ = false;
121
122 if (run_loop_) {
123 run_loop_->Quit();
124 }
125 }
126}
127
128void AppRemotingConnectionHelper::ConnectionReady(bool ready) {
129 DCHECK(thread_checker_.CalledOnValidThread());
130
131 if (ready) {
132 SendClientConnectionDetailsToHost();
133 } else {
134 // We will only get called here with a false value for |ready| if the video
135 // renderer encounters an error.
136 connection_is_ready_for_tests_ = false;
137
138 if (run_loop_) {
139 run_loop_->Quit();
140 }
141 }
142}
143
144void AppRemotingConnectionHelper::HostMessageReceived(
145 const protocol::ExtensionMessage& message) {
146 DCHECK(thread_checker_.CalledOnValidThread());
147
148 // If a callback is not registered, then the message is passed to a default
149 // handler for the class based on the message type.
150 if (!host_message_received_callback_.is_null()) {
151 host_message_received_callback_.Run(message);
152 } else if (message.type() == "onWindowAdded") {
153 HandleOnWindowAddedMessage(message);
154 } else {
joedow9cfae7c2015-07-06 18:44:18155 VLOG(2) << "HostMessage not handled by HostMessageReceived().";
156 VLOG(2) << "type: " << message.type();
157 VLOG(2) << "data: " << message.data();
liaoyuke8b8cfafb2015-06-29 20:41:20158 }
159}
160
161void AppRemotingConnectionHelper::SendClientConnectionDetailsToHost() {
162 // First send an access token which will be used for Google Drive access.
163 protocol::ExtensionMessage message;
164 message.set_type("accessToken");
165 message.set_data(AppRemotingSharedData->access_token());
166
joedow9cfae7c2015-07-06 18:44:18167 VLOG(1) << "Sending access token to host";
liaoyuke8b8cfafb2015-06-29 20:41:20168 client_->host_stub()->DeliverClientMessage(message);
169
170 // Next send the host a description of the client screen size.
171 protocol::ClientResolution client_resolution;
172 client_resolution.set_width(kDefaultWidth);
173 client_resolution.set_height(kDefaultHeight);
174 client_resolution.set_x_dpi(kDefaultDPI);
175 client_resolution.set_y_dpi(kDefaultDPI);
176 client_resolution.set_dips_width(kDefaultWidth);
177 client_resolution.set_dips_height(kDefaultHeight);
178
joedow9cfae7c2015-07-06 18:44:18179 VLOG(1) << "Sending ClientResolution details to host";
liaoyuke8b8cfafb2015-06-29 20:41:20180 client_->host_stub()->NotifyClientResolution(client_resolution);
181
182 // Finally send a message to start sending us video packets.
183 protocol::VideoControl video_control;
184 video_control.set_enable(true);
185
joedow9cfae7c2015-07-06 18:44:18186 VLOG(1) << "Sending enable VideoControl message to host";
liaoyuke8b8cfafb2015-06-29 20:41:20187 client_->host_stub()->ControlVideo(video_control);
188}
189
190void AppRemotingConnectionHelper::HandleOnWindowAddedMessage(
191 const remoting::protocol::ExtensionMessage& message) {
192 DCHECK_EQ(message.type(), "onWindowAdded");
193
194 const base::DictionaryValue* message_data = nullptr;
195 scoped_ptr<base::Value> host_message = base::JSONReader::Read(message.data());
196 if (!host_message.get() || !host_message->GetAsDictionary(&message_data)) {
197 LOG(ERROR) << "onWindowAdded message received was not valid JSON.";
198 if (run_loop_) {
199 run_loop_->Quit();
200 }
201 return;
202 }
203
204 std::string current_window_title;
205 message_data->GetString("title", &current_window_title);
206 if (current_window_title == kHostProcessWindowTitle) {
207 LOG(ERROR) << "Host Process Window is visible, this likely means that the "
208 << "underlying application is in a bad state, YMMV.";
209 }
210
211 std::string main_window_title = application_details_.main_window_title;
212 if (current_window_title.find_first_of(main_window_title) == 0) {
213 connection_is_ready_for_tests_ = true;
214
215 if (timer_->IsRunning()) {
216 timer_->Stop();
217 }
218
219 DCHECK(run_loop_);
220 // Now that the main window is visible, give the app some time to settle
221 // before signaling that it is ready to run tests.
222 timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(2),
223 run_loop_->QuitClosure());
224 }
225}
226
227} // namespace test
228} // namespace remoting