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
18namespace extensions {
19
20// Contains info relevant to a pending API request.
21struct 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
33RequestSender::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
41RequestSender::ScopedTabID::~ScopedTabID() {
42  DCHECK_EQ(tab_id_, request_sender_->source_tab_id_);
43  request_sender_->source_tab_id_ = previous_tab_id_;
44}
45
46RequestSender::RequestSender(Dispatcher* dispatcher)
47    : dispatcher_(dispatcher), source_tab_id_(-1) {}
48
49RequestSender::~RequestSender() {}
50
51void 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
57linked_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
66int RequestSender::GetNextRequestId() const {
67  static int next_request_id = 0;
68  return next_request_id++;
69}
70
71void 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
125void 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
141void 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