1 // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "tests/cefclient/browser/urlrequest_test.h"
6
7 #include <memory>
8 #include <string>
9
10 #include "include/base/cef_callback.h"
11 #include "include/base/cef_logging.h"
12 #include "include/cef_urlrequest.h"
13 #include "include/wrapper/cef_helpers.h"
14 #include "tests/cefclient/browser/test_runner.h"
15
16 namespace client {
17 namespace urlrequest_test {
18
19 namespace {
20
21 const char kTestUrlPath[] = "/urlrequest";
22 const char kTestMessageName[] = "URLRequestTest";
23
24 // Implementation of CefURLRequestClient that stores response information. Only
25 // accessed on the UI thread.
26 class RequestClient : public CefURLRequestClient {
27 public:
28 // Callback to be executed on request completion.
29 using Callback =
30 base::OnceCallback<void(CefURLRequest::ErrorCode /*error_code*/,
31 const std::string& /*download_data*/)>;
32
RequestClient(Callback callback)33 explicit RequestClient(Callback callback) : callback_(std::move(callback)) {
34 CEF_REQUIRE_UI_THREAD();
35 DCHECK(!callback_.is_null());
36 }
37
Detach()38 void Detach() {
39 CEF_REQUIRE_UI_THREAD();
40 if (!callback_.is_null())
41 callback_.Reset();
42 }
43
OnRequestComplete(CefRefPtr<CefURLRequest> request)44 void OnRequestComplete(CefRefPtr<CefURLRequest> request) override {
45 CEF_REQUIRE_UI_THREAD();
46 if (!callback_.is_null()) {
47 std::move(callback_).Run(request->GetRequestError(), download_data_);
48 }
49 }
50
OnUploadProgress(CefRefPtr<CefURLRequest> request,int64 current,int64 total)51 void OnUploadProgress(CefRefPtr<CefURLRequest> request,
52 int64 current,
53 int64 total) override {}
54
OnDownloadProgress(CefRefPtr<CefURLRequest> request,int64 current,int64 total)55 void OnDownloadProgress(CefRefPtr<CefURLRequest> request,
56 int64 current,
57 int64 total) override {}
58
OnDownloadData(CefRefPtr<CefURLRequest> request,const void * data,size_t data_length)59 void OnDownloadData(CefRefPtr<CefURLRequest> request,
60 const void* data,
61 size_t data_length) override {
62 CEF_REQUIRE_UI_THREAD();
63 download_data_ += std::string(static_cast<const char*>(data), data_length);
64 }
65
GetAuthCredentials(bool isProxy,const CefString & host,int port,const CefString & realm,const CefString & scheme,CefRefPtr<CefAuthCallback> callback)66 bool GetAuthCredentials(bool isProxy,
67 const CefString& host,
68 int port,
69 const CefString& realm,
70 const CefString& scheme,
71 CefRefPtr<CefAuthCallback> callback) override {
72 return false;
73 }
74
75 private:
76 Callback callback_;
77 std::string download_data_;
78
79 IMPLEMENT_REFCOUNTING(RequestClient);
80 DISALLOW_COPY_AND_ASSIGN(RequestClient);
81 };
82
83 // Handle messages in the browser process. Only accessed on the UI thread.
84 class Handler : public CefMessageRouterBrowserSide::Handler {
85 public:
Handler()86 Handler() { CEF_REQUIRE_UI_THREAD(); }
87
~Handler()88 ~Handler() { CancelPendingRequest(); }
89
90 // Called due to cefQuery execution in urlrequest.html.
OnQuery(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int64 query_id,const CefString & request,bool persistent,CefRefPtr<Callback> callback)91 bool OnQuery(CefRefPtr<CefBrowser> browser,
92 CefRefPtr<CefFrame> frame,
93 int64 query_id,
94 const CefString& request,
95 bool persistent,
96 CefRefPtr<Callback> callback) override {
97 CEF_REQUIRE_UI_THREAD();
98
99 // Only handle messages from the test URL.
100 const std::string& url = frame->GetURL();
101 if (!test_runner::IsTestURL(url, kTestUrlPath))
102 return false;
103
104 const std::string& message_name = request;
105 if (message_name.find(kTestMessageName) == 0) {
106 const std::string& load_url =
107 message_name.substr(sizeof(kTestMessageName));
108
109 CancelPendingRequest();
110
111 DCHECK(!callback_.get());
112 DCHECK(!urlrequest_.get());
113
114 callback_ = callback;
115
116 // Create a CefRequest for the specified URL.
117 CefRefPtr<CefRequest> cef_request = CefRequest::Create();
118 cef_request->SetURL(load_url);
119 cef_request->SetMethod("GET");
120
121 // Callback to be executed on request completion.
122 // It's safe to use base::Unretained() here because there is only one
123 // RequestClient pending at any given time and we explicitly detach the
124 // callback in the Handler destructor.
125 auto request_callback =
126 base::BindOnce(&Handler::OnRequestComplete, base::Unretained(this));
127
128 // Create and start a new CefURLRequest associated with the frame, so
129 // that it shares authentication with ClientHandler::GetAuthCredentials.
130 urlrequest_ = frame->CreateURLRequest(
131 cef_request, new RequestClient(std::move(request_callback)));
132
133 return true;
134 }
135
136 return false;
137 }
138
139 private:
140 // Cancel the currently pending URL request, if any.
CancelPendingRequest()141 void CancelPendingRequest() {
142 CEF_REQUIRE_UI_THREAD();
143
144 if (urlrequest_.get()) {
145 // Don't execute the callback when we explicitly cancel the request.
146 static_cast<RequestClient*>(urlrequest_->GetClient().get())->Detach();
147
148 urlrequest_->Cancel();
149 urlrequest_ = nullptr;
150 }
151
152 if (callback_.get()) {
153 // Must always execute |callback_| before deleting it.
154 callback_->Failure(ERR_ABORTED, test_runner::GetErrorString(ERR_ABORTED));
155 callback_ = nullptr;
156 }
157 }
158
OnRequestComplete(CefURLRequest::ErrorCode error_code,const std::string & download_data)159 void OnRequestComplete(CefURLRequest::ErrorCode error_code,
160 const std::string& download_data) {
161 CEF_REQUIRE_UI_THREAD();
162
163 if (error_code == ERR_NONE)
164 callback_->Success(download_data);
165 else
166 callback_->Failure(error_code, test_runner::GetErrorString(error_code));
167
168 callback_ = nullptr;
169 urlrequest_ = nullptr;
170 }
171
172 CefRefPtr<Callback> callback_;
173 CefRefPtr<CefURLRequest> urlrequest_;
174
175 DISALLOW_COPY_AND_ASSIGN(Handler);
176 };
177
178 } // namespace
179
CreateMessageHandlers(test_runner::MessageHandlerSet & handlers)180 void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
181 handlers.insert(new Handler());
182 }
183
184 } // namespace urlrequest_test
185 } // namespace client
186