blob: ca0fa4092422bc3cbdbc624c94910cbe1f05be45 [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
45void AppRemotingConnectionHelper::Initialize() {
46 client_.reset(new TestChromotingClient());
47 client_->AddRemoteConnectionObserver(this);
48}
49
50void AppRemotingConnectionHelper::SetHostMessageReceivedCallback(
51 HostMessageReceivedCallback host_message_received_callback) {
52 host_message_received_callback_ = host_message_received_callback;
53}
54
55void AppRemotingConnectionHelper::ResetHostMessageReceivedCallback() {
56 host_message_received_callback_.Reset();
57}
58
59bool AppRemotingConnectionHelper::StartConnection() {
60 DCHECK(thread_checker_.CalledOnValidThread());
61
62 RemoteHostInfo remote_host_info;
63 remoting::test::AppRemotingSharedData->GetRemoteHostInfoForApplicationId(
64 application_details_.application_id, &remote_host_info);
65
66 if (!remote_host_info.IsReadyForConnection()) {
67 return false;
68 }
69
70 DCHECK(!run_loop_ || !run_loop_->running());
71 run_loop_.reset(new base::RunLoop());
72
73 // We will wait up to 30 seconds to complete the remote connection and for the
74 // main application window to become visible.
75 DCHECK(!timer_->IsRunning());
76 timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(30),
77 run_loop_->QuitClosure());
78
79 client_->StartConnection(AppRemotingSharedData->user_name(),
80 AppRemotingSharedData->access_token(),
81 remote_host_info);
82
83 run_loop_->Run();
84 timer_->Stop();
85
86 if (connection_is_ready_for_tests_) {
87 return true;
88 } else {
89 client_->EndConnection();
90 return false;
91 }
92}
93
94protocol::ClipboardStub* AppRemotingConnectionHelper::clipboard_forwarder() {
95 return client_->clipboard_forwarder();
96}
97
98protocol::HostStub* AppRemotingConnectionHelper::host_stub() {
99 return client_->host_stub();
100}
101
102protocol::InputStub* AppRemotingConnectionHelper::input_stub() {
103 return client_->input_stub();
104}
105
106void AppRemotingConnectionHelper::ConnectionStateChanged(
107 protocol::ConnectionToHost::State state,
108 protocol::ErrorCode error_code) {
109 DCHECK(thread_checker_.CalledOnValidThread());
110
111 // If the connection is closed or failed then mark the connection as closed
112 // and quit the current RunLoop if it exists.
113 if (state == protocol::ConnectionToHost::CLOSED ||
114 state == protocol::ConnectionToHost::FAILED ||
115 error_code != protocol::OK) {
116 connection_is_ready_for_tests_ = false;
117
118 if (run_loop_) {
119 run_loop_->Quit();
120 }
121 }
122}
123
124void AppRemotingConnectionHelper::ConnectionReady(bool ready) {
125 DCHECK(thread_checker_.CalledOnValidThread());
126
127 if (ready) {
128 SendClientConnectionDetailsToHost();
129 } else {
130 // We will only get called here with a false value for |ready| if the video
131 // renderer encounters an error.
132 connection_is_ready_for_tests_ = false;
133
134 if (run_loop_) {
135 run_loop_->Quit();
136 }
137 }
138}
139
140void AppRemotingConnectionHelper::HostMessageReceived(
141 const protocol::ExtensionMessage& message) {
142 DCHECK(thread_checker_.CalledOnValidThread());
143
144 // If a callback is not registered, then the message is passed to a default
145 // handler for the class based on the message type.
146 if (!host_message_received_callback_.is_null()) {
147 host_message_received_callback_.Run(message);
148 } else if (message.type() == "onWindowAdded") {
149 HandleOnWindowAddedMessage(message);
150 } else {
151 DVLOG(2) << "HostMessage not handled by HostMessageReceived().";
152 DVLOG(2) << "type: " << message.type();
153 DVLOG(2) << "data: " << message.data();
154 }
155}
156
157void AppRemotingConnectionHelper::SendClientConnectionDetailsToHost() {
158 // First send an access token which will be used for Google Drive access.
159 protocol::ExtensionMessage message;
160 message.set_type("accessToken");
161 message.set_data(AppRemotingSharedData->access_token());
162
163 DVLOG(1) << "Sending access token to host";
164 client_->host_stub()->DeliverClientMessage(message);
165
166 // Next send the host a description of the client screen size.
167 protocol::ClientResolution client_resolution;
168 client_resolution.set_width(kDefaultWidth);
169 client_resolution.set_height(kDefaultHeight);
170 client_resolution.set_x_dpi(kDefaultDPI);
171 client_resolution.set_y_dpi(kDefaultDPI);
172 client_resolution.set_dips_width(kDefaultWidth);
173 client_resolution.set_dips_height(kDefaultHeight);
174
175 DVLOG(1) << "Sending ClientResolution details to host";
176 client_->host_stub()->NotifyClientResolution(client_resolution);
177
178 // Finally send a message to start sending us video packets.
179 protocol::VideoControl video_control;
180 video_control.set_enable(true);
181
182 DVLOG(1) << "Sending enable VideoControl message to host";
183 client_->host_stub()->ControlVideo(video_control);
184}
185
186void AppRemotingConnectionHelper::HandleOnWindowAddedMessage(
187 const remoting::protocol::ExtensionMessage& message) {
188 DCHECK_EQ(message.type(), "onWindowAdded");
189
190 const base::DictionaryValue* message_data = nullptr;
191 scoped_ptr<base::Value> host_message = base::JSONReader::Read(message.data());
192 if (!host_message.get() || !host_message->GetAsDictionary(&message_data)) {
193 LOG(ERROR) << "onWindowAdded message received was not valid JSON.";
194 if (run_loop_) {
195 run_loop_->Quit();
196 }
197 return;
198 }
199
200 std::string current_window_title;
201 message_data->GetString("title", &current_window_title);
202 if (current_window_title == kHostProcessWindowTitle) {
203 LOG(ERROR) << "Host Process Window is visible, this likely means that the "
204 << "underlying application is in a bad state, YMMV.";
205 }
206
207 std::string main_window_title = application_details_.main_window_title;
208 if (current_window_title.find_first_of(main_window_title) == 0) {
209 connection_is_ready_for_tests_ = true;
210
211 if (timer_->IsRunning()) {
212 timer_->Stop();
213 }
214
215 DCHECK(run_loop_);
216 // Now that the main window is visible, give the app some time to settle
217 // before signaling that it is ready to run tests.
218 timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(2),
219 run_loop_->QuitClosure());
220 }
221}
222
223} // namespace test
224} // namespace remoting