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