1 // Copyright (c) 2014 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 "include/wrapper/cef_message_router.h"
6
7 #include <map>
8 #include <set>
9
10 #include "include/base/cef_callback.h"
11 #include "include/cef_task.h"
12 #include "include/wrapper/cef_closure_task.h"
13 #include "include/wrapper/cef_helpers.h"
14 #include "libcef_dll/wrapper/cef_browser_info_map.h"
15
16 namespace {
17
18 // ID value reserved for internal use.
19 const int kReservedId = 0;
20
21 // Appended to the JS function name for related IPC messages.
22 const char kMessageSuffix[] = "Msg";
23
24 // JS object member argument names for cefQuery.
25 const char kMemberRequest[] = "request";
26 const char kMemberOnSuccess[] = "onSuccess";
27 const char kMemberOnFailure[] = "onFailure";
28 const char kMemberPersistent[] = "persistent";
29
30 // Default error information when a query is canceled.
31 const int kCanceledErrorCode = -1;
32 const char kCanceledErrorMessage[] = "The query has been canceled";
33
34 // Validate configuration settings.
ValidateConfig(CefMessageRouterConfig & config)35 bool ValidateConfig(CefMessageRouterConfig& config) {
36 // Must specify function names.
37 if (config.js_cancel_function.empty() || config.js_query_function.empty()) {
38 return false;
39 }
40
41 return true;
42 }
43
44 // Helper template for generated ID values.
45 template <typename T>
46 class IdGenerator {
47 public:
IdGenerator()48 IdGenerator() : next_id_(kReservedId) {}
49
50 IdGenerator(const IdGenerator&) = delete;
51 IdGenerator& operator=(const IdGenerator&) = delete;
52
GetNextId()53 T GetNextId() {
54 T id = ++next_id_;
55 if (id == kReservedId) // In case the integer value wraps.
56 id = ++next_id_;
57 return id;
58 }
59
60 private:
61 T next_id_;
62 };
63
64 // Browser-side router implementation.
65 class CefMessageRouterBrowserSideImpl : public CefMessageRouterBrowserSide {
66 public:
67 // Implementation of the Callback interface.
68 class CallbackImpl : public CefMessageRouterBrowserSide::Callback {
69 public:
CallbackImpl(CefRefPtr<CefMessageRouterBrowserSideImpl> router,int browser_id,int64 query_id,bool persistent)70 CallbackImpl(CefRefPtr<CefMessageRouterBrowserSideImpl> router,
71 int browser_id,
72 int64 query_id,
73 bool persistent)
74 : router_(router),
75 browser_id_(browser_id),
76 query_id_(query_id),
77 persistent_(persistent) {}
78
79 CallbackImpl(const CallbackImpl&) = delete;
80 CallbackImpl& operator=(const CallbackImpl&) = delete;
81
~CallbackImpl()82 virtual ~CallbackImpl() {
83 // Hitting this DCHECK means that you didn't call Success or Failure
84 // on the Callback after returning true from Handler::OnQuery. You must
85 // call Failure to terminate persistent queries.
86 DCHECK(!router_);
87 }
88
Success(const CefString & response)89 void Success(const CefString& response) override {
90 if (!CefCurrentlyOn(TID_UI)) {
91 // Must execute on the UI thread to access member variables.
92 CefPostTask(TID_UI,
93 base::BindOnce(&CallbackImpl::Success, this, response));
94 return;
95 }
96
97 if (router_) {
98 CefPostTask(
99 TID_UI,
100 base::BindOnce(&CefMessageRouterBrowserSideImpl::OnCallbackSuccess,
101 router_.get(), browser_id_, query_id_, response));
102
103 if (!persistent_) {
104 // Non-persistent callbacks are only good for a single use.
105 router_ = nullptr;
106 }
107 }
108 }
109
Failure(int error_code,const CefString & error_message)110 void Failure(int error_code, const CefString& error_message) override {
111 if (!CefCurrentlyOn(TID_UI)) {
112 // Must execute on the UI thread to access member variables.
113 CefPostTask(TID_UI, base::BindOnce(&CallbackImpl::Failure, this,
114 error_code, error_message));
115 return;
116 }
117
118 if (router_) {
119 CefPostTask(
120 TID_UI,
121 base::BindOnce(&CefMessageRouterBrowserSideImpl::OnCallbackFailure,
122 router_.get(), browser_id_, query_id_, error_code,
123 error_message));
124
125 // Failure always invalidates the callback.
126 router_ = nullptr;
127 }
128 }
129
Detach()130 void Detach() {
131 CEF_REQUIRE_UI_THREAD();
132 router_ = nullptr;
133 }
134
135 private:
136 CefRefPtr<CefMessageRouterBrowserSideImpl> router_;
137 const int browser_id_;
138 const int64 query_id_;
139 const bool persistent_;
140
141 IMPLEMENT_REFCOUNTING(CallbackImpl);
142 };
143
CefMessageRouterBrowserSideImpl(const CefMessageRouterConfig & config)144 explicit CefMessageRouterBrowserSideImpl(const CefMessageRouterConfig& config)
145 : config_(config),
146 query_message_name_(config.js_query_function.ToString() +
147 kMessageSuffix),
148 cancel_message_name_(config.js_cancel_function.ToString() +
149 kMessageSuffix) {}
150
151 CefMessageRouterBrowserSideImpl(const CefMessageRouterBrowserSideImpl&) =
152 delete;
153 CefMessageRouterBrowserSideImpl& operator=(
154 const CefMessageRouterBrowserSideImpl&) = delete;
155
~CefMessageRouterBrowserSideImpl()156 virtual ~CefMessageRouterBrowserSideImpl() {
157 // There should be no pending queries when the router is deleted.
158 DCHECK(browser_query_info_map_.empty());
159 }
160
AddHandler(Handler * handler,bool first)161 bool AddHandler(Handler* handler, bool first) override {
162 CEF_REQUIRE_UI_THREAD();
163 if (handler_set_.find(handler) == handler_set_.end()) {
164 handler_set_.insert(first ? handler_set_.begin() : handler_set_.end(),
165 handler);
166 return true;
167 }
168 return false;
169 }
170
RemoveHandler(Handler * handler)171 bool RemoveHandler(Handler* handler) override {
172 CEF_REQUIRE_UI_THREAD();
173 if (handler_set_.erase(handler) > 0) {
174 CancelPendingFor(nullptr, handler, true);
175 return true;
176 }
177 return false;
178 }
179
CancelPending(CefRefPtr<CefBrowser> browser,Handler * handler)180 void CancelPending(CefRefPtr<CefBrowser> browser, Handler* handler) override {
181 CancelPendingFor(browser, handler, true);
182 }
183
GetPendingCount(CefRefPtr<CefBrowser> browser,Handler * handler)184 int GetPendingCount(CefRefPtr<CefBrowser> browser,
185 Handler* handler) override {
186 CEF_REQUIRE_UI_THREAD();
187
188 if (browser_query_info_map_.empty())
189 return 0;
190
191 if (handler) {
192 // Need to iterate over each QueryInfo object to test the handler.
193 class Visitor : public BrowserQueryInfoMap::Visitor {
194 public:
195 explicit Visitor(Handler* handler) : handler_(handler), count_(0) {}
196
197 bool OnNextInfo(int browser_id,
198 InfoIdType info_id,
199 InfoObjectType info,
200 bool* remove) override {
201 if (info->handler == handler_)
202 count_++;
203 return true;
204 }
205
206 int count() const { return count_; }
207
208 private:
209 Handler* handler_;
210 int count_;
211 };
212
213 Visitor visitor(handler);
214
215 if (browser.get()) {
216 // Count queries associated with the specified browser.
217 browser_query_info_map_.FindAll(browser->GetIdentifier(), &visitor);
218 } else {
219 // Count all queries for all browsers.
220 browser_query_info_map_.FindAll(&visitor);
221 }
222
223 return visitor.count();
224 } else if (browser.get()) {
225 return static_cast<int>(
226 browser_query_info_map_.size(browser->GetIdentifier()));
227 }
228
229 return static_cast<int>(browser_query_info_map_.size());
230 }
231
OnBeforeClose(CefRefPtr<CefBrowser> browser)232 void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
233 CancelPendingFor(browser, nullptr, false);
234 }
235
OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser)236 void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser) override {
237 CancelPendingFor(browser, nullptr, false);
238 }
239
OnBeforeBrowse(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame)240 void OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
241 CefRefPtr<CefFrame> frame) override {
242 if (frame->IsMain())
243 CancelPendingFor(browser, nullptr, false);
244 }
245
OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefProcessId source_process,CefRefPtr<CefProcessMessage> message)246 bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
247 CefRefPtr<CefFrame> frame,
248 CefProcessId source_process,
249 CefRefPtr<CefProcessMessage> message) override {
250 CEF_REQUIRE_UI_THREAD();
251
252 const std::string& message_name = message->GetName();
253 if (message_name == query_message_name_) {
254 CefRefPtr<CefListValue> args = message->GetArgumentList();
255 DCHECK_EQ(args->GetSize(), 4U);
256
257 const int context_id = args->GetInt(0);
258 const int request_id = args->GetInt(1);
259 const CefString& request = args->GetString(2);
260 const bool persistent = args->GetBool(3);
261
262 if (handler_set_.empty()) {
263 // No handlers so cancel the query.
264 CancelUnhandledQuery(browser, frame, context_id, request_id);
265 return true;
266 }
267
268 const int browser_id = browser->GetIdentifier();
269 const int64 query_id = query_id_generator_.GetNextId();
270
271 CefRefPtr<CallbackImpl> callback(
272 new CallbackImpl(this, browser_id, query_id, persistent));
273
274 // Make a copy of the handler list in case the user adds or removes a
275 // handler while we're iterating.
276 HandlerSet handler_set = handler_set_;
277
278 bool handled = false;
279 HandlerSet::const_iterator it_handler = handler_set.begin();
280 for (; it_handler != handler_set.end(); ++it_handler) {
281 handled = (*it_handler)
282 ->OnQuery(browser, frame, query_id, request, persistent,
283 callback.get());
284 if (handled)
285 break;
286 }
287
288 // If the query isn't handled nothing should be keeping a reference to
289 // the callback.
290 DCHECK(handled || callback->HasOneRef());
291
292 if (handled) {
293 // Persist the query information until the callback executes.
294 // It's safe to do this here because the callback will execute
295 // asynchronously.
296 QueryInfo* info = new QueryInfo;
297 info->browser = browser;
298 info->frame = frame;
299 info->context_id = context_id;
300 info->request_id = request_id;
301 info->persistent = persistent;
302 info->callback = callback;
303 info->handler = *(it_handler);
304 browser_query_info_map_.Add(browser_id, query_id, info);
305 } else {
306 // Invalidate the callback.
307 callback->Detach();
308
309 // No one chose to handle the query so cancel it.
310 CancelUnhandledQuery(browser, frame, context_id, request_id);
311 }
312
313 return true;
314 } else if (message_name == cancel_message_name_) {
315 CefRefPtr<CefListValue> args = message->GetArgumentList();
316 DCHECK_EQ(args->GetSize(), 2U);
317
318 const int browser_id = browser->GetIdentifier();
319 const int context_id = args->GetInt(0);
320 const int request_id = args->GetInt(1);
321
322 CancelPendingRequest(browser_id, context_id, request_id);
323 return true;
324 }
325
326 return false;
327 }
328
329 private:
330 // Structure representing a pending query.
331 struct QueryInfo {
332 // Browser and frame originated the query.
333 CefRefPtr<CefBrowser> browser;
334 CefRefPtr<CefFrame> frame;
335
336 // IDs that uniquely identify the query in the renderer process. These
337 // values are opaque to the browser process but must be returned with the
338 // response.
339 int context_id;
340 int request_id;
341
342 // True if the query is persistent.
343 bool persistent;
344
345 // Callback associated with the query that must be detached when the query
346 // is canceled.
347 CefRefPtr<CallbackImpl> callback;
348
349 // Handler that should be notified if the query is automatically canceled.
350 Handler* handler;
351 };
352
353 // Retrieve a QueryInfo object from the map based on the browser-side query
354 // ID. If |always_remove| is true then the QueryInfo object will always be
355 // removed from the map. Othewise, the QueryInfo object will only be removed
356 // if the query is non-persistent. If |removed| is true the caller is
357 // responsible for deleting the returned QueryInfo object.
GetQueryInfo(int browser_id,int64 query_id,bool always_remove,bool * removed)358 QueryInfo* GetQueryInfo(int browser_id,
359 int64 query_id,
360 bool always_remove,
361 bool* removed) {
362 class Visitor : public BrowserQueryInfoMap::Visitor {
363 public:
364 explicit Visitor(bool always_remove)
365 : always_remove_(always_remove), removed_(false) {}
366
367 bool OnNextInfo(int browser_id,
368 InfoIdType info_id,
369 InfoObjectType info,
370 bool* remove) override {
371 *remove = removed_ = (always_remove_ || !info->persistent);
372 return true;
373 }
374
375 bool removed() const { return removed_; }
376
377 private:
378 const bool always_remove_;
379 bool removed_;
380 };
381
382 Visitor visitor(always_remove);
383 QueryInfo* info =
384 browser_query_info_map_.Find(browser_id, query_id, &visitor);
385 if (info)
386 *removed = visitor.removed();
387 return info;
388 }
389
390 // Called by CallbackImpl on success.
OnCallbackSuccess(int browser_id,int64 query_id,const CefString & response)391 void OnCallbackSuccess(int browser_id,
392 int64 query_id,
393 const CefString& response) {
394 CEF_REQUIRE_UI_THREAD();
395
396 bool removed;
397 QueryInfo* info = GetQueryInfo(browser_id, query_id, false, &removed);
398 if (info) {
399 SendQuerySuccess(info, response);
400 if (removed)
401 delete info;
402 }
403 }
404
405 // Called by CallbackImpl on failure.
OnCallbackFailure(int browser_id,int64 query_id,int error_code,const CefString & error_message)406 void OnCallbackFailure(int browser_id,
407 int64 query_id,
408 int error_code,
409 const CefString& error_message) {
410 CEF_REQUIRE_UI_THREAD();
411
412 bool removed;
413 QueryInfo* info = GetQueryInfo(browser_id, query_id, true, &removed);
414 if (info) {
415 SendQueryFailure(info, error_code, error_message);
416 DCHECK(removed);
417 delete info;
418 }
419 }
420
SendQuerySuccess(QueryInfo * info,const CefString & response)421 void SendQuerySuccess(QueryInfo* info, const CefString& response) {
422 SendQuerySuccess(info->browser, info->frame, info->context_id,
423 info->request_id, response);
424 }
425
SendQuerySuccess(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int context_id,int request_id,const CefString & response)426 void SendQuerySuccess(CefRefPtr<CefBrowser> browser,
427 CefRefPtr<CefFrame> frame,
428 int context_id,
429 int request_id,
430 const CefString& response) {
431 CefRefPtr<CefProcessMessage> message =
432 CefProcessMessage::Create(query_message_name_);
433 CefRefPtr<CefListValue> args = message->GetArgumentList();
434 args->SetInt(0, context_id);
435 args->SetInt(1, request_id);
436 args->SetBool(2, true); // Indicates a success result.
437 args->SetString(3, response);
438 frame->SendProcessMessage(PID_RENDERER, message);
439 }
440
SendQueryFailure(QueryInfo * info,int error_code,const CefString & error_message)441 void SendQueryFailure(QueryInfo* info,
442 int error_code,
443 const CefString& error_message) {
444 SendQueryFailure(info->browser, info->frame, info->context_id,
445 info->request_id, error_code, error_message);
446 }
447
SendQueryFailure(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int context_id,int request_id,int error_code,const CefString & error_message)448 void SendQueryFailure(CefRefPtr<CefBrowser> browser,
449 CefRefPtr<CefFrame> frame,
450 int context_id,
451 int request_id,
452 int error_code,
453 const CefString& error_message) {
454 CefRefPtr<CefProcessMessage> message =
455 CefProcessMessage::Create(query_message_name_);
456 CefRefPtr<CefListValue> args = message->GetArgumentList();
457 args->SetInt(0, context_id);
458 args->SetInt(1, request_id);
459 args->SetBool(2, false); // Indicates a failure result.
460 args->SetInt(3, error_code);
461 args->SetString(4, error_message);
462 frame->SendProcessMessage(PID_RENDERER, message);
463 }
464
465 // Cancel a query that has not been sent to a handler.
CancelUnhandledQuery(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int context_id,int request_id)466 void CancelUnhandledQuery(CefRefPtr<CefBrowser> browser,
467 CefRefPtr<CefFrame> frame,
468 int context_id,
469 int request_id) {
470 SendQueryFailure(browser, frame, context_id, request_id, kCanceledErrorCode,
471 kCanceledErrorMessage);
472 }
473
474 // Cancel a query that has already been sent to a handler.
CancelQuery(int64 query_id,QueryInfo * info,bool notify_renderer)475 void CancelQuery(int64 query_id, QueryInfo* info, bool notify_renderer) {
476 if (notify_renderer)
477 SendQueryFailure(info, kCanceledErrorCode, kCanceledErrorMessage);
478
479 info->handler->OnQueryCanceled(info->browser, info->frame, query_id);
480
481 // Invalidate the callback.
482 info->callback->Detach();
483 }
484
485 // Cancel all pending queries associated with either |browser| or |handler|.
486 // If both |browser| and |handler| are NULL all pending queries will be
487 // canceled. Set |notify_renderer| to true if the renderer should be notified.
CancelPendingFor(CefRefPtr<CefBrowser> browser,Handler * handler,bool notify_renderer)488 void CancelPendingFor(CefRefPtr<CefBrowser> browser,
489 Handler* handler,
490 bool notify_renderer) {
491 if (!CefCurrentlyOn(TID_UI)) {
492 // Must execute on the UI thread.
493 CefPostTask(
494 TID_UI,
495 base::BindOnce(&CefMessageRouterBrowserSideImpl::CancelPendingFor,
496 this, browser, handler, notify_renderer));
497 return;
498 }
499
500 if (browser_query_info_map_.empty())
501 return;
502
503 class Visitor : public BrowserQueryInfoMap::Visitor {
504 public:
505 Visitor(CefMessageRouterBrowserSideImpl* router,
506 Handler* handler,
507 bool notify_renderer)
508 : router_(router),
509 handler_(handler),
510 notify_renderer_(notify_renderer) {}
511
512 bool OnNextInfo(int browser_id,
513 InfoIdType info_id,
514 InfoObjectType info,
515 bool* remove) override {
516 if (!handler_ || info->handler == handler_) {
517 *remove = true;
518 router_->CancelQuery(info_id, info, notify_renderer_);
519 delete info;
520 }
521 return true;
522 }
523
524 private:
525 CefMessageRouterBrowserSideImpl* router_;
526 Handler* handler_;
527 const bool notify_renderer_;
528 };
529
530 Visitor visitor(this, handler, notify_renderer);
531
532 if (browser.get()) {
533 // Cancel all queries associated with the specified browser.
534 browser_query_info_map_.FindAll(browser->GetIdentifier(), &visitor);
535 } else {
536 // Cancel all queries for all browsers.
537 browser_query_info_map_.FindAll(&visitor);
538 }
539 }
540
541 // Cancel a query based on the renderer-side IDs. If |request_id| is
542 // kReservedId all requests associated with |context_id| will be canceled.
CancelPendingRequest(int browser_id,int context_id,int request_id)543 void CancelPendingRequest(int browser_id, int context_id, int request_id) {
544 class Visitor : public BrowserQueryInfoMap::Visitor {
545 public:
546 Visitor(CefMessageRouterBrowserSideImpl* router,
547 int context_id,
548 int request_id)
549 : router_(router), context_id_(context_id), request_id_(request_id) {}
550
551 bool OnNextInfo(int browser_id,
552 InfoIdType info_id,
553 InfoObjectType info,
554 bool* remove) override {
555 if (info->context_id == context_id_ &&
556 (request_id_ == kReservedId || info->request_id == request_id_)) {
557 *remove = true;
558 router_->CancelQuery(info_id, info, false);
559 delete info;
560
561 // Stop iterating if only canceling a single request.
562 return (request_id_ == kReservedId);
563 }
564 return true;
565 }
566
567 private:
568 CefMessageRouterBrowserSideImpl* router_;
569 const int context_id_;
570 const int request_id_;
571 };
572
573 Visitor visitor(this, context_id, request_id);
574 browser_query_info_map_.FindAll(browser_id, &visitor);
575 }
576
577 const CefMessageRouterConfig config_;
578 const std::string query_message_name_;
579 const std::string cancel_message_name_;
580
581 IdGenerator<int64> query_id_generator_;
582
583 // Set of currently registered handlers. An entry is added when a handler is
584 // registered and removed when a handler is unregistered.
585 using HandlerSet = std::set<Handler*>;
586 HandlerSet handler_set_;
587
588 // Map of query ID to QueryInfo instance. An entry is added when a Handler
589 // indicates that it will handle the query and removed when either the query
590 // is completed via the Callback, the query is explicitly canceled from the
591 // renderer process, or the associated context is (or will be) released.
592 using BrowserQueryInfoMap = CefBrowserInfoMap<int64, QueryInfo*>;
593 BrowserQueryInfoMap browser_query_info_map_;
594 };
595
596 // Renderer-side router implementation.
597 class CefMessageRouterRendererSideImpl : public CefMessageRouterRendererSide {
598 public:
599 class V8HandlerImpl : public CefV8Handler {
600 public:
V8HandlerImpl(CefRefPtr<CefMessageRouterRendererSideImpl> router,const CefMessageRouterConfig & config)601 V8HandlerImpl(CefRefPtr<CefMessageRouterRendererSideImpl> router,
602 const CefMessageRouterConfig& config)
603 : router_(router), config_(config), context_id_(kReservedId) {}
604
605 V8HandlerImpl(const V8HandlerImpl&) = delete;
606 V8HandlerImpl& operator=(const V8HandlerImpl&) = delete;
607
Execute(const CefString & name,CefRefPtr<CefV8Value> object,const CefV8ValueList & arguments,CefRefPtr<CefV8Value> & retval,CefString & exception)608 bool Execute(const CefString& name,
609 CefRefPtr<CefV8Value> object,
610 const CefV8ValueList& arguments,
611 CefRefPtr<CefV8Value>& retval,
612 CefString& exception) override {
613 if (name == config_.js_query_function) {
614 if (arguments.size() != 1 || !arguments[0]->IsObject()) {
615 exception = "Invalid arguments; expecting a single object";
616 return true;
617 }
618
619 CefRefPtr<CefV8Value> arg = arguments[0];
620
621 CefRefPtr<CefV8Value> requestVal = arg->GetValue(kMemberRequest);
622 if (!requestVal.get() || !requestVal->IsString()) {
623 exception = "Invalid arguments; object member '" +
624 std::string(kMemberRequest) +
625 "' is required and must have type string";
626 return true;
627 }
628
629 CefRefPtr<CefV8Value> successVal = nullptr;
630 if (arg->HasValue(kMemberOnSuccess)) {
631 successVal = arg->GetValue(kMemberOnSuccess);
632 if (!successVal->IsFunction()) {
633 exception = "Invalid arguments; object member '" +
634 std::string(kMemberOnSuccess) +
635 "' must have type function";
636 return true;
637 }
638 }
639
640 CefRefPtr<CefV8Value> failureVal = nullptr;
641 if (arg->HasValue(kMemberOnFailure)) {
642 failureVal = arg->GetValue(kMemberOnFailure);
643 if (!failureVal->IsFunction()) {
644 exception = "Invalid arguments; object member '" +
645 std::string(kMemberOnFailure) +
646 "' must have type function";
647 return true;
648 }
649 }
650
651 CefRefPtr<CefV8Value> persistentVal = nullptr;
652 if (arg->HasValue(kMemberPersistent)) {
653 persistentVal = arg->GetValue(kMemberPersistent);
654 if (!persistentVal->IsBool()) {
655 exception = "Invalid arguments; object member '" +
656 std::string(kMemberPersistent) +
657 "' must have type boolean";
658 return true;
659 }
660 }
661
662 CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
663 const int context_id = GetIDForContext(context);
664 const bool persistent =
665 (persistentVal.get() && persistentVal->GetBoolValue());
666
667 const int request_id = router_->SendQuery(
668 context->GetBrowser(), context->GetFrame(), context_id,
669 requestVal->GetStringValue(), persistent, successVal, failureVal);
670 retval = CefV8Value::CreateInt(request_id);
671 return true;
672 } else if (name == config_.js_cancel_function) {
673 if (arguments.size() != 1 || !arguments[0]->IsInt()) {
674 exception = "Invalid arguments; expecting a single integer";
675 return true;
676 }
677
678 bool result = false;
679 const int request_id = arguments[0]->GetIntValue();
680 if (request_id != kReservedId) {
681 CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
682 const int context_id = GetIDForContext(context);
683 result =
684 router_->SendCancel(context->GetBrowser(), context->GetFrame(),
685 context_id, request_id);
686 }
687 retval = CefV8Value::CreateBool(result);
688 return true;
689 }
690
691 return false;
692 }
693
694 private:
695 // Don't create the context ID until it's actually needed.
GetIDForContext(CefRefPtr<CefV8Context> context)696 int GetIDForContext(CefRefPtr<CefV8Context> context) {
697 if (context_id_ == kReservedId)
698 context_id_ = router_->CreateIDForContext(context);
699 return context_id_;
700 }
701
702 CefRefPtr<CefMessageRouterRendererSideImpl> router_;
703 const CefMessageRouterConfig config_;
704 int context_id_;
705
706 IMPLEMENT_REFCOUNTING(V8HandlerImpl);
707 };
708
CefMessageRouterRendererSideImpl(const CefMessageRouterConfig & config)709 explicit CefMessageRouterRendererSideImpl(
710 const CefMessageRouterConfig& config)
711 : config_(config),
712 query_message_name_(config.js_query_function.ToString() +
713 kMessageSuffix),
714 cancel_message_name_(config.js_cancel_function.ToString() +
715 kMessageSuffix) {}
716
717 CefMessageRouterRendererSideImpl(const CefMessageRouterRendererSideImpl&) =
718 delete;
719 CefMessageRouterRendererSideImpl& operator=(
720 const CefMessageRouterRendererSideImpl&) = delete;
721
GetPendingCount(CefRefPtr<CefBrowser> browser,CefRefPtr<CefV8Context> context)722 int GetPendingCount(CefRefPtr<CefBrowser> browser,
723 CefRefPtr<CefV8Context> context) override {
724 CEF_REQUIRE_RENDERER_THREAD();
725
726 if (browser_request_info_map_.empty())
727 return 0;
728
729 if (context.get()) {
730 const int context_id = GetIDForContext(context, false);
731 if (context_id == kReservedId)
732 return 0; // Nothing associated with the specified context.
733
734 // Need to iterate over each RequestInfo object to test the context.
735 class Visitor : public BrowserRequestInfoMap::Visitor {
736 public:
737 explicit Visitor(int context_id) : context_id_(context_id), count_(0) {}
738
739 bool OnNextInfo(int browser_id,
740 InfoIdType info_id,
741 InfoObjectType info,
742 bool* remove) override {
743 if (info_id.first == context_id_)
744 count_++;
745 return true;
746 }
747
748 int count() const { return count_; }
749
750 private:
751 int context_id_;
752 int count_;
753 };
754
755 Visitor visitor(context_id);
756
757 if (browser.get()) {
758 // Count requests associated with the specified browser.
759 browser_request_info_map_.FindAll(browser->GetIdentifier(), &visitor);
760 } else {
761 // Count all requests for all browsers.
762 browser_request_info_map_.FindAll(&visitor);
763 }
764
765 return visitor.count();
766 } else if (browser.get()) {
767 return static_cast<int>(
768 browser_request_info_map_.size(browser->GetIdentifier()));
769 }
770
771 return static_cast<int>(browser_request_info_map_.size());
772 }
773
OnContextCreated(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefV8Context> context)774 void OnContextCreated(CefRefPtr<CefBrowser> browser,
775 CefRefPtr<CefFrame> frame,
776 CefRefPtr<CefV8Context> context) override {
777 CEF_REQUIRE_RENDERER_THREAD();
778
779 // Register function handlers with the 'window' object.
780 CefRefPtr<CefV8Value> window = context->GetGlobal();
781
782 CefRefPtr<V8HandlerImpl> handler = new V8HandlerImpl(this, config_);
783 CefV8Value::PropertyAttribute attributes =
784 static_cast<CefV8Value::PropertyAttribute>(
785 V8_PROPERTY_ATTRIBUTE_READONLY | V8_PROPERTY_ATTRIBUTE_DONTENUM |
786 V8_PROPERTY_ATTRIBUTE_DONTDELETE);
787
788 // Add the query function.
789 CefRefPtr<CefV8Value> query_func =
790 CefV8Value::CreateFunction(config_.js_query_function, handler.get());
791 window->SetValue(config_.js_query_function, query_func, attributes);
792
793 // Add the cancel function.
794 CefRefPtr<CefV8Value> cancel_func =
795 CefV8Value::CreateFunction(config_.js_cancel_function, handler.get());
796 window->SetValue(config_.js_cancel_function, cancel_func, attributes);
797 }
798
OnContextReleased(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefV8Context> context)799 void OnContextReleased(CefRefPtr<CefBrowser> browser,
800 CefRefPtr<CefFrame> frame,
801 CefRefPtr<CefV8Context> context) override {
802 CEF_REQUIRE_RENDERER_THREAD();
803
804 // Get the context ID and remove the context from the map.
805 const int context_id = GetIDForContext(context, true);
806 if (context_id != kReservedId) {
807 // Cancel all pending requests for the context.
808 SendCancel(browser, frame, context_id, kReservedId);
809 }
810 }
811
OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefProcessId source_process,CefRefPtr<CefProcessMessage> message)812 bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
813 CefRefPtr<CefFrame> frame,
814 CefProcessId source_process,
815 CefRefPtr<CefProcessMessage> message) override {
816 CEF_REQUIRE_RENDERER_THREAD();
817
818 const std::string& message_name = message->GetName();
819 if (message_name == query_message_name_) {
820 CefRefPtr<CefListValue> args = message->GetArgumentList();
821 DCHECK_GT(args->GetSize(), 3U);
822
823 const int context_id = args->GetInt(0);
824 const int request_id = args->GetInt(1);
825 bool is_success = args->GetBool(2);
826
827 if (is_success) {
828 DCHECK_EQ(args->GetSize(), 4U);
829 const CefString& response = args->GetString(3);
830 CefPostTask(
831 TID_RENDERER,
832 base::BindOnce(
833 &CefMessageRouterRendererSideImpl::ExecuteSuccessCallback, this,
834 browser->GetIdentifier(), context_id, request_id, response));
835 } else {
836 DCHECK_EQ(args->GetSize(), 5U);
837 int error_code = args->GetInt(3);
838 const CefString& error_message = args->GetString(4);
839 CefPostTask(
840 TID_RENDERER,
841 base::BindOnce(
842 &CefMessageRouterRendererSideImpl::ExecuteFailureCallback, this,
843 browser->GetIdentifier(), context_id, request_id, error_code,
844 error_message));
845 }
846
847 return true;
848 }
849
850 return false;
851 }
852
853 private:
854 // Structure representing a pending request.
855 struct RequestInfo {
856 // True if the request is persistent.
857 bool persistent;
858
859 // Success callback function. May be NULL.
860 CefRefPtr<CefV8Value> success_callback;
861
862 // Failure callback function. May be NULL.
863 CefRefPtr<CefV8Value> failure_callback;
864 };
865
866 // Retrieve a RequestInfo object from the map based on the renderer-side
867 // IDs. If |always_remove| is true then the RequestInfo object will always be
868 // removed from the map. Othewise, the RequestInfo object will only be removed
869 // if the query is non-persistent. If |removed| is true the caller is
870 // responsible for deleting the returned QueryInfo object.
GetRequestInfo(int browser_id,int context_id,int request_id,bool always_remove,bool * removed)871 RequestInfo* GetRequestInfo(int browser_id,
872 int context_id,
873 int request_id,
874 bool always_remove,
875 bool* removed) {
876 class Visitor : public BrowserRequestInfoMap::Visitor {
877 public:
878 explicit Visitor(bool always_remove)
879 : always_remove_(always_remove), removed_(false) {}
880
881 bool OnNextInfo(int browser_id,
882 InfoIdType info_id,
883 InfoObjectType info,
884 bool* remove) override {
885 *remove = removed_ = (always_remove_ || !info->persistent);
886 return true;
887 }
888
889 bool removed() const { return removed_; }
890
891 private:
892 const bool always_remove_;
893 bool removed_;
894 };
895
896 Visitor visitor(always_remove);
897 RequestInfo* info = browser_request_info_map_.Find(
898 browser_id, std::make_pair(context_id, request_id), &visitor);
899 if (info)
900 *removed = visitor.removed();
901 return info;
902 }
903
904 // Returns the new request ID.
SendQuery(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int context_id,const CefString & request,bool persistent,CefRefPtr<CefV8Value> success_callback,CefRefPtr<CefV8Value> failure_callback)905 int SendQuery(CefRefPtr<CefBrowser> browser,
906 CefRefPtr<CefFrame> frame,
907 int context_id,
908 const CefString& request,
909 bool persistent,
910 CefRefPtr<CefV8Value> success_callback,
911 CefRefPtr<CefV8Value> failure_callback) {
912 CEF_REQUIRE_RENDERER_THREAD();
913
914 const int request_id = request_id_generator_.GetNextId();
915
916 RequestInfo* info = new RequestInfo;
917 info->persistent = persistent;
918 info->success_callback = success_callback;
919 info->failure_callback = failure_callback;
920 browser_request_info_map_.Add(browser->GetIdentifier(),
921 std::make_pair(context_id, request_id), info);
922
923 CefRefPtr<CefProcessMessage> message =
924 CefProcessMessage::Create(query_message_name_);
925
926 CefRefPtr<CefListValue> args = message->GetArgumentList();
927 args->SetInt(0, context_id);
928 args->SetInt(1, request_id);
929 args->SetString(2, request);
930 args->SetBool(3, persistent);
931
932 frame->SendProcessMessage(PID_BROWSER, message);
933
934 return request_id;
935 }
936
937 // If |request_id| is kReservedId all requests associated with |context_id|
938 // will be canceled, otherwise only the specified |request_id| will be
939 // canceled. Returns true if any request was canceled.
SendCancel(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int context_id,int request_id)940 bool SendCancel(CefRefPtr<CefBrowser> browser,
941 CefRefPtr<CefFrame> frame,
942 int context_id,
943 int request_id) {
944 CEF_REQUIRE_RENDERER_THREAD();
945
946 const int browser_id = browser->GetIdentifier();
947
948 int cancel_count = 0;
949 if (request_id != kReservedId) {
950 // Cancel a single request.
951 bool removed;
952 RequestInfo* info =
953 GetRequestInfo(browser_id, context_id, request_id, true, &removed);
954 if (info) {
955 DCHECK(removed);
956 delete info;
957 cancel_count = 1;
958 }
959 } else {
960 // Cancel all requests with the specified context ID.
961 class Visitor : public BrowserRequestInfoMap::Visitor {
962 public:
963 explicit Visitor(int context_id)
964 : context_id_(context_id), cancel_count_(0) {}
965
966 bool OnNextInfo(int browser_id,
967 InfoIdType info_id,
968 InfoObjectType info,
969 bool* remove) override {
970 if (info_id.first == context_id_) {
971 *remove = true;
972 delete info;
973 cancel_count_++;
974 }
975 return true;
976 }
977
978 int cancel_count() const { return cancel_count_; }
979
980 private:
981 const int context_id_;
982 int cancel_count_;
983 };
984
985 Visitor visitor(context_id);
986 browser_request_info_map_.FindAll(browser_id, &visitor);
987 cancel_count = visitor.cancel_count();
988 }
989
990 if (cancel_count > 0) {
991 CefRefPtr<CefProcessMessage> message =
992 CefProcessMessage::Create(cancel_message_name_);
993
994 CefRefPtr<CefListValue> args = message->GetArgumentList();
995 args->SetInt(0, context_id);
996 args->SetInt(1, request_id);
997
998 frame->SendProcessMessage(PID_BROWSER, message);
999 return true;
1000 }
1001
1002 return false;
1003 }
1004
1005 // Execute the onSuccess JavaScript callback.
ExecuteSuccessCallback(int browser_id,int context_id,int request_id,const CefString & response)1006 void ExecuteSuccessCallback(int browser_id,
1007 int context_id,
1008 int request_id,
1009 const CefString& response) {
1010 CEF_REQUIRE_RENDERER_THREAD();
1011
1012 bool removed;
1013 RequestInfo* info =
1014 GetRequestInfo(browser_id, context_id, request_id, false, &removed);
1015 if (!info)
1016 return;
1017
1018 CefRefPtr<CefV8Context> context = GetContextByID(context_id);
1019 if (context && info->success_callback) {
1020 CefV8ValueList args;
1021 args.push_back(CefV8Value::CreateString(response));
1022 info->success_callback->ExecuteFunctionWithContext(context, nullptr,
1023 args);
1024 }
1025
1026 if (removed)
1027 delete info;
1028 }
1029
1030 // Execute the onFailure JavaScript callback.
ExecuteFailureCallback(int browser_id,int context_id,int request_id,int error_code,const CefString & error_message)1031 void ExecuteFailureCallback(int browser_id,
1032 int context_id,
1033 int request_id,
1034 int error_code,
1035 const CefString& error_message) {
1036 CEF_REQUIRE_RENDERER_THREAD();
1037
1038 bool removed;
1039 RequestInfo* info =
1040 GetRequestInfo(browser_id, context_id, request_id, true, &removed);
1041 if (!info)
1042 return;
1043
1044 CefRefPtr<CefV8Context> context = GetContextByID(context_id);
1045 if (context && info->failure_callback) {
1046 CefV8ValueList args;
1047 args.push_back(CefV8Value::CreateInt(error_code));
1048 args.push_back(CefV8Value::CreateString(error_message));
1049 info->failure_callback->ExecuteFunctionWithContext(context, nullptr,
1050 args);
1051 }
1052
1053 DCHECK(removed);
1054 delete info;
1055 }
1056
CreateIDForContext(CefRefPtr<CefV8Context> context)1057 int CreateIDForContext(CefRefPtr<CefV8Context> context) {
1058 CEF_REQUIRE_RENDERER_THREAD();
1059
1060 // The context should not already have an associated ID.
1061 DCHECK_EQ(GetIDForContext(context, false), kReservedId);
1062
1063 const int context_id = context_id_generator_.GetNextId();
1064 context_map_.insert(std::make_pair(context_id, context));
1065 return context_id;
1066 }
1067
1068 // Retrieves the existing ID value associated with the specified |context|.
1069 // If |remove| is true the context will also be removed from the map.
GetIDForContext(CefRefPtr<CefV8Context> context,bool remove)1070 int GetIDForContext(CefRefPtr<CefV8Context> context, bool remove) {
1071 CEF_REQUIRE_RENDERER_THREAD();
1072
1073 ContextMap::iterator it = context_map_.begin();
1074 for (; it != context_map_.end(); ++it) {
1075 if (it->second->IsSame(context)) {
1076 int context_id = it->first;
1077 if (remove)
1078 context_map_.erase(it);
1079 return context_id;
1080 }
1081 }
1082
1083 return kReservedId;
1084 }
1085
GetContextByID(int context_id)1086 CefRefPtr<CefV8Context> GetContextByID(int context_id) {
1087 CEF_REQUIRE_RENDERER_THREAD();
1088
1089 ContextMap::const_iterator it = context_map_.find(context_id);
1090 if (it != context_map_.end())
1091 return it->second;
1092 return nullptr;
1093 }
1094
1095 const CefMessageRouterConfig config_;
1096 const std::string query_message_name_;
1097 const std::string cancel_message_name_;
1098
1099 IdGenerator<int> context_id_generator_;
1100 IdGenerator<int> request_id_generator_;
1101
1102 // Map of (request ID, context ID) to RequestInfo for pending queries. An
1103 // entry is added when a request is initiated via the bound function and
1104 // removed when either the request completes, is canceled via the bound
1105 // function, or the associated context is released.
1106 using BrowserRequestInfoMap =
1107 CefBrowserInfoMap<std::pair<int, int>, RequestInfo*>;
1108 BrowserRequestInfoMap browser_request_info_map_;
1109
1110 // Map of context ID to CefV8Context for existing contexts. An entry is added
1111 // when a bound function is executed for the first time in the context and
1112 // removed when the context is released.
1113 using ContextMap = std::map<int, CefRefPtr<CefV8Context>>;
1114 ContextMap context_map_;
1115 };
1116
1117 } // namespace
1118
CefMessageRouterConfig()1119 CefMessageRouterConfig::CefMessageRouterConfig()
1120 : js_query_function("cefQuery"), js_cancel_function("cefQueryCancel") {}
1121
1122 // static
Create(const CefMessageRouterConfig & config)1123 CefRefPtr<CefMessageRouterBrowserSide> CefMessageRouterBrowserSide::Create(
1124 const CefMessageRouterConfig& config) {
1125 CefMessageRouterConfig validated_config = config;
1126 if (!ValidateConfig(validated_config))
1127 return nullptr;
1128 return new CefMessageRouterBrowserSideImpl(validated_config);
1129 }
1130
1131 // static
Create(const CefMessageRouterConfig & config)1132 CefRefPtr<CefMessageRouterRendererSide> CefMessageRouterRendererSide::Create(
1133 const CefMessageRouterConfig& config) {
1134 CefMessageRouterConfig validated_config = config;
1135 if (!ValidateConfig(validated_config))
1136 return nullptr;
1137 return new CefMessageRouterRendererSideImpl(validated_config);
1138 }
1139