• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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