Home | History | Annotate | Download | only in renderer
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "extensions/renderer/request_sender.h"
      6 
      7 #include "base/values.h"
      8 #include "content/public/renderer/render_view.h"
      9 #include "extensions/common/extension_messages.h"
     10 #include "extensions/renderer/dispatcher.h"
     11 #include "extensions/renderer/script_context.h"
     12 #include "third_party/WebKit/public/web/WebDocument.h"
     13 #include "third_party/WebKit/public/web/WebFrame.h"
     14 #include "third_party/WebKit/public/web/WebScopedUserGesture.h"
     15 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
     16 #include "third_party/WebKit/public/web/WebUserGestureToken.h"
     17 
     18 namespace extensions {
     19 
     20 // Contains info relevant to a pending API request.
     21 struct PendingRequest {
     22  public:
     23   PendingRequest(const std::string& name,
     24                  RequestSender::Source* source,
     25                  blink::WebUserGestureToken token)
     26       : name(name), source(source), token(token) {}
     27 
     28   std::string name;
     29   RequestSender::Source* source;
     30   blink::WebUserGestureToken token;
     31 };
     32 
     33 RequestSender::ScopedTabID::ScopedTabID(RequestSender* request_sender,
     34                                         int tab_id)
     35     : request_sender_(request_sender),
     36       tab_id_(tab_id),
     37       previous_tab_id_(request_sender->source_tab_id_) {
     38   request_sender_->source_tab_id_ = tab_id;
     39 }
     40 
     41 RequestSender::ScopedTabID::~ScopedTabID() {
     42   DCHECK_EQ(tab_id_, request_sender_->source_tab_id_);
     43   request_sender_->source_tab_id_ = previous_tab_id_;
     44 }
     45 
     46 RequestSender::RequestSender(Dispatcher* dispatcher)
     47     : dispatcher_(dispatcher), source_tab_id_(-1) {}
     48 
     49 RequestSender::~RequestSender() {}
     50 
     51 void RequestSender::InsertRequest(int request_id,
     52                                   PendingRequest* pending_request) {
     53   DCHECK_EQ(0u, pending_requests_.count(request_id));
     54   pending_requests_[request_id].reset(pending_request);
     55 }
     56 
     57 linked_ptr<PendingRequest> RequestSender::RemoveRequest(int request_id) {
     58   PendingRequestMap::iterator i = pending_requests_.find(request_id);
     59   if (i == pending_requests_.end())
     60     return linked_ptr<PendingRequest>();
     61   linked_ptr<PendingRequest> result = i->second;
     62   pending_requests_.erase(i);
     63   return result;
     64 }
     65 
     66 int RequestSender::GetNextRequestId() const {
     67   static int next_request_id = 0;
     68   return next_request_id++;
     69 }
     70 
     71 void RequestSender::StartRequest(Source* source,
     72                                  const std::string& name,
     73                                  int request_id,
     74                                  bool has_callback,
     75                                  bool for_io_thread,
     76                                  base::ListValue* value_args) {
     77   ScriptContext* context = source->GetContext();
     78   if (!context)
     79     return;
     80 
     81   // Get the current RenderView so that we can send a routed IPC message from
     82   // the correct source.
     83   content::RenderView* renderview = context->GetRenderView();
     84   if (!renderview)
     85     return;
     86 
     87   const std::set<std::string>& function_names = dispatcher_->function_names();
     88   if (function_names.find(name) == function_names.end()) {
     89     NOTREACHED()
     90         << "Unexpected function " << name
     91         << ". Did you remember to register it with ExtensionFunctionRegistry?";
     92     return;
     93   }
     94 
     95   // TODO(koz): See if we can make this a CHECK.
     96   if (!dispatcher_->CheckContextAccessToExtensionAPI(name, context))
     97     return;
     98 
     99   GURL source_url;
    100   if (blink::WebFrame* webframe = context->web_frame())
    101     source_url = webframe->document().url();
    102 
    103   InsertRequest(request_id, new PendingRequest(name, source,
    104       blink::WebUserGestureIndicator::currentUserGestureToken()));
    105 
    106   ExtensionHostMsg_Request_Params params;
    107   params.name = name;
    108   params.arguments.Swap(value_args);
    109   params.extension_id = context->GetExtensionID();
    110   params.source_url = source_url;
    111   params.source_tab_id = source_tab_id_;
    112   params.request_id = request_id;
    113   params.has_callback = has_callback;
    114   params.user_gesture =
    115       blink::WebUserGestureIndicator::isProcessingUserGesture();
    116   if (for_io_thread) {
    117     renderview->Send(new ExtensionHostMsg_RequestForIOThread(
    118         renderview->GetRoutingID(), params));
    119   } else {
    120     renderview->Send(
    121         new ExtensionHostMsg_Request(renderview->GetRoutingID(), params));
    122   }
    123 }
    124 
    125 void RequestSender::HandleResponse(int request_id,
    126                                    bool success,
    127                                    const base::ListValue& response,
    128                                    const std::string& error) {
    129   linked_ptr<PendingRequest> request = RemoveRequest(request_id);
    130 
    131   if (!request.get()) {
    132     // This can happen if a context is destroyed while a request is in flight.
    133     return;
    134   }
    135 
    136   blink::WebScopedUserGesture gesture(request->token);
    137   request->source->OnResponseReceived(
    138       request->name, request_id, success, response, error);
    139 }
    140 
    141 void RequestSender::InvalidateSource(Source* source) {
    142   for (PendingRequestMap::iterator it = pending_requests_.begin();
    143        it != pending_requests_.end();) {
    144     if (it->second->source == source)
    145       pending_requests_.erase(it++);
    146     else
    147       ++it;
    148   }
    149 }
    150 
    151 }  // namespace extensions
    152