1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/renderer/request_sender.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/timer/elapsed_timer.h"
#include "base/values.h"
#include "content/public/renderer/render_frame.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension_messages.h"
#include "extensions/renderer/script_context.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebScopedUserGesture.h"
#include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
#include "third_party/WebKit/public/web/WebUserGestureToken.h"
namespace extensions {
// Contains info relevant to a pending API request.
struct PendingRequest {
public:
PendingRequest(const std::string& name,
RequestSender::Source* source,
blink::WebUserGestureToken token)
: name(name), source(source), token(token) {}
std::string name;
RequestSender::Source* source;
blink::WebUserGestureToken token;
};
RequestSender::RequestSender() {}
RequestSender::~RequestSender() {}
void RequestSender::InsertRequest(
int request_id,
std::unique_ptr<PendingRequest> pending_request) {
DCHECK_EQ(0u, pending_requests_.count(request_id));
pending_requests_[request_id] = std::move(pending_request);
}
std::unique_ptr<PendingRequest> RequestSender::RemoveRequest(int request_id) {
PendingRequestMap::iterator i = pending_requests_.find(request_id);
if (i == pending_requests_.end())
return std::unique_ptr<PendingRequest>();
std::unique_ptr<PendingRequest> result = std::move(i->second);
pending_requests_.erase(i);
return result;
}
int RequestSender::GetNextRequestId() const {
static int next_request_id = 0;
return next_request_id++;
}
bool RequestSender::StartRequest(Source* source,
const std::string& name,
int request_id,
bool has_callback,
bool for_io_thread,
base::ListValue* value_args) {
ScriptContext* context = source->GetContext();
if (!context)
return false;
bool for_service_worker =
context->context_type() == Feature::SERVICE_WORKER_CONTEXT;
// Get the current RenderFrame so that we can send a routed IPC message from
// the correct source.
// Note that |render_frame| would be nullptr for Service Workers. Service
// Workers use control IPC instead.
content::RenderFrame* render_frame = context->GetRenderFrame();
if (!for_service_worker && !render_frame) {
// It is important to early exit here for non Service Worker contexts so
// that we do not create orphaned PendingRequests below.
return false;
}
// TODO(koz): See if we can make this a CHECK.
if (!context->HasAccessOrThrowError(name))
return false;
GURL source_url;
if (blink::WebLocalFrame* webframe = context->web_frame())
source_url = webframe->document().url();
InsertRequest(request_id,
base::MakeUnique<PendingRequest>(
name, source,
blink::WebUserGestureIndicator::currentUserGestureToken()));
ExtensionHostMsg_Request_Params params;
params.name = name;
params.arguments.Swap(value_args);
params.extension_id = context->GetExtensionID();
params.source_url = source_url;
params.request_id = request_id;
params.has_callback = has_callback;
params.user_gesture =
blink::WebUserGestureIndicator::isProcessingUserGestureThreadSafe();
// Set Service Worker specific params to default values.
params.worker_thread_id = -1;
params.service_worker_version_id = kInvalidServiceWorkerVersionId;
SendRequest(render_frame, for_io_thread, params);
return true;
}
void RequestSender::SendRequest(content::RenderFrame* render_frame,
bool for_io_thread,
ExtensionHostMsg_Request_Params& params) {
if (for_io_thread) {
render_frame->Send(new ExtensionHostMsg_RequestForIOThread(
render_frame->GetRoutingID(), params));
} else {
render_frame->Send(
new ExtensionHostMsg_Request(render_frame->GetRoutingID(), params));
}
}
void RequestSender::HandleResponse(int request_id,
bool success,
const base::ListValue& response,
const std::string& error) {
base::ElapsedTimer timer;
std::unique_ptr<PendingRequest> request = RemoveRequest(request_id);
if (!request.get()) {
// This can happen if a context is destroyed while a request is in flight.
return;
}
// TODO(devlin): Would it be useful to partition this data based on
// extension function once we have a suitable baseline? crbug.com/608561.
blink::WebScopedUserGesture gesture(request->token);
request->source->OnResponseReceived(
request->name, request_id, success, response, error);
UMA_HISTOGRAM_TIMES("Extensions.Functions.HandleResponseElapsedTime",
timer.Elapsed());
}
void RequestSender::InvalidateSource(Source* source) {
for (PendingRequestMap::iterator it = pending_requests_.begin();
it != pending_requests_.end();) {
if (it->second->source == source)
pending_requests_.erase(it++);
else
++it;
}
}
} // namespace extensions
|