| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 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/api_request_handler.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/guid.h" | |
| 9 #include "base/memory/ptr_util.h" | |
| 10 #include "base/values.h" | |
| 11 #include "content/public/child/v8_value_converter.h" | |
| 12 #include "gin/converter.h" | |
| 13 #include "gin/data_object_builder.h" | |
| 14 #include "third_party/WebKit/public/web/WebScopedUserGesture.h" | |
| 15 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h" | |
| 16 | |
| 17 namespace extensions { | |
| 18 | |
| 19 APIRequestHandler::Request::Request() {} | |
| 20 APIRequestHandler::Request::~Request() = default; | |
| 21 | |
| 22 APIRequestHandler::PendingRequest::PendingRequest( | |
| 23 v8::Isolate* isolate, | |
| 24 v8::Local<v8::Function> callback, | |
| 25 v8::Local<v8::Context> context, | |
| 26 const std::vector<v8::Local<v8::Value>>& local_callback_args) | |
| 27 : isolate(isolate), | |
| 28 context(isolate, context), | |
| 29 callback(isolate, callback), | |
| 30 user_gesture_token( | |
| 31 blink::WebUserGestureIndicator::CurrentUserGestureToken()) { | |
| 32 if (!local_callback_args.empty()) { | |
| 33 callback_arguments.reserve(local_callback_args.size()); | |
| 34 for (const auto& arg : local_callback_args) | |
| 35 callback_arguments.push_back(v8::Global<v8::Value>(isolate, arg)); | |
| 36 } | |
| 37 } | |
| 38 | |
| 39 APIRequestHandler::PendingRequest::~PendingRequest() {} | |
| 40 APIRequestHandler::PendingRequest::PendingRequest(PendingRequest&&) = default; | |
| 41 APIRequestHandler::PendingRequest& APIRequestHandler::PendingRequest::operator=( | |
| 42 PendingRequest&&) = default; | |
| 43 | |
| 44 APIRequestHandler::APIRequestHandler(const SendRequestMethod& send_request, | |
| 45 const CallJSFunction& call_js, | |
| 46 APILastError last_error) | |
| 47 : send_request_(send_request), | |
| 48 call_js_(call_js), | |
| 49 last_error_(std::move(last_error)) {} | |
| 50 | |
| 51 APIRequestHandler::~APIRequestHandler() {} | |
| 52 | |
| 53 int APIRequestHandler::StartRequest(v8::Local<v8::Context> context, | |
| 54 const std::string& method, | |
| 55 std::unique_ptr<base::ListValue> arguments, | |
| 56 v8::Local<v8::Function> callback, | |
| 57 v8::Local<v8::Function> custom_callback, | |
| 58 binding::RequestThread thread) { | |
| 59 auto request = base::MakeUnique<Request>(); | |
| 60 | |
| 61 // The request id is primarily used in the renderer to associate an API | |
| 62 // request with the associated callback, but it's also used in the browser as | |
| 63 // an identifier for the extension function (e.g. by the pageCapture API). | |
| 64 // TODO(devlin): We should probably fix this, since the request id is only | |
| 65 // unique per-isolate, rather than globally. | |
| 66 // TODO(devlin): We could *probably* get away with just using an integer | |
| 67 // here, but it's a little less foolproof. How slow is GenerateGUID? Should | |
| 68 // we use that instead? It means updating the IPC | |
| 69 // (ExtensionHostMsg_Request). | |
| 70 // base::UnguessableToken is another good option. | |
| 71 int request_id = next_request_id_++; | |
| 72 request->request_id = request_id; | |
| 73 | |
| 74 if (!custom_callback.IsEmpty() || !callback.IsEmpty()) { | |
| 75 v8::Isolate* isolate = context->GetIsolate(); | |
| 76 // In the JS bindings, custom callbacks are called with the arguments of | |
| 77 // name, the full request object (see below), the original callback, and | |
| 78 // the responses from the API. The responses from the API are handled by the | |
| 79 // APIRequestHandler, but we need to curry in the other values. | |
| 80 std::vector<v8::Local<v8::Value>> callback_args; | |
| 81 if (!custom_callback.IsEmpty()) { | |
| 82 // TODO(devlin): The |request| object in the JS bindings includes | |
| 83 // properties for callback, callbackSchema, args, stack, id, and | |
| 84 // customCallback. Of those, it appears that we only use stack, args, and | |
| 85 // id (since callback is curried in separately). We may be able to update | |
| 86 // bindings to get away from some of those. For now, just pass in an | |
| 87 // object with the request id. | |
| 88 v8::Local<v8::Object> request = | |
| 89 gin::DataObjectBuilder(isolate).Set("id", request_id).Build(); | |
| 90 v8::Local<v8::Value> callback_to_pass = callback; | |
| 91 if (callback_to_pass.IsEmpty()) | |
| 92 callback_to_pass = v8::Undefined(isolate); | |
| 93 callback_args = {gin::StringToSymbol(isolate, method), request, | |
| 94 callback_to_pass}; | |
| 95 callback = custom_callback; | |
| 96 } | |
| 97 | |
| 98 request->has_callback = true; | |
| 99 pending_requests_.insert(std::make_pair( | |
| 100 request_id, PendingRequest(isolate, callback, context, callback_args))); | |
| 101 } | |
| 102 | |
| 103 request->has_user_gesture = | |
| 104 blink::WebUserGestureIndicator::IsProcessingUserGestureThreadSafe(); | |
| 105 request->arguments = std::move(arguments); | |
| 106 request->method_name = method; | |
| 107 request->thread = thread; | |
| 108 | |
| 109 send_request_.Run(std::move(request), context); | |
| 110 return request_id; | |
| 111 } | |
| 112 | |
| 113 void APIRequestHandler::CompleteRequest(int request_id, | |
| 114 const base::ListValue& response_args, | |
| 115 const std::string& error) { | |
| 116 auto iter = pending_requests_.find(request_id); | |
| 117 // The request may have been removed if the context was invalidated before a | |
| 118 // response is ready. | |
| 119 if (iter == pending_requests_.end()) | |
| 120 return; | |
| 121 | |
| 122 PendingRequest pending_request = std::move(iter->second); | |
| 123 pending_requests_.erase(iter); | |
| 124 | |
| 125 v8::Isolate* isolate = pending_request.isolate; | |
| 126 v8::HandleScope handle_scope(isolate); | |
| 127 v8::Local<v8::Context> context = pending_request.context.Get(isolate); | |
| 128 v8::Context::Scope context_scope(context); | |
| 129 std::unique_ptr<content::V8ValueConverter> converter = | |
| 130 content::V8ValueConverter::Create(); | |
| 131 std::vector<v8::Local<v8::Value>> args; | |
| 132 args.reserve(response_args.GetSize() + | |
| 133 pending_request.callback_arguments.size()); | |
| 134 for (const auto& arg : pending_request.callback_arguments) | |
| 135 args.push_back(arg.Get(isolate)); | |
| 136 for (const auto& arg : response_args) | |
| 137 args.push_back(converter->ToV8Value(&arg, context)); | |
| 138 | |
| 139 blink::WebScopedUserGesture user_gesture(pending_request.user_gesture_token); | |
| 140 if (!error.empty()) | |
| 141 last_error_.SetError(context, error); | |
| 142 | |
| 143 // args.size() is converted to int, but args is controlled by chrome and is | |
| 144 // never close to std::numeric_limits<int>::max. | |
| 145 call_js_.Run(pending_request.callback.Get(isolate), context, args.size(), | |
| 146 args.data()); | |
| 147 | |
| 148 if (!error.empty()) | |
| 149 last_error_.ClearError(context, true); | |
| 150 } | |
| 151 | |
| 152 int APIRequestHandler::AddPendingRequest(v8::Local<v8::Context> context, | |
| 153 v8::Local<v8::Function> callback) { | |
| 154 int request_id = next_request_id_++; | |
| 155 pending_requests_.emplace( | |
| 156 request_id, PendingRequest(context->GetIsolate(), callback, context, | |
| 157 std::vector<v8::Local<v8::Value>>())); | |
| 158 return request_id; | |
| 159 } | |
| 160 | |
| 161 void APIRequestHandler::InvalidateContext(v8::Local<v8::Context> context) { | |
| 162 for (auto iter = pending_requests_.begin(); | |
| 163 iter != pending_requests_.end();) { | |
| 164 if (iter->second.context == context) | |
| 165 iter = pending_requests_.erase(iter); | |
| 166 else | |
| 167 ++iter; | |
| 168 } | |
| 169 } | |
| 170 | |
| 171 std::set<int> APIRequestHandler::GetPendingRequestIdsForTesting() const { | |
| 172 std::set<int> result; | |
| 173 for (const auto& pair : pending_requests_) | |
| 174 result.insert(pair.first); | |
| 175 return result; | |
| 176 } | |
| 177 | |
| 178 } // namespace extensions | |
| OLD | NEW |