• 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_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