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