Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2012 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 "chrome/renderer/extensions/extension_helper.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/command_line.h"
     10 #include "base/json/json_string_value_serializer.h"
     11 #include "base/lazy_instance.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "chrome/common/chrome_switches.h"
     15 #include "chrome/common/extensions/api/messaging/message.h"
     16 #include "chrome/common/extensions/extension_constants.h"
     17 #include "chrome/common/extensions/extension_messages.h"
     18 #include "chrome/common/render_messages.h"
     19 #include "chrome/common/url_constants.h"
     20 #include "chrome/renderer/extensions/chrome_v8_context.h"
     21 #include "chrome/renderer/extensions/console.h"
     22 #include "chrome/renderer/extensions/dispatcher.h"
     23 #include "chrome/renderer/extensions/messaging_bindings.h"
     24 #include "chrome/renderer/extensions/user_script_scheduler.h"
     25 #include "chrome/renderer/extensions/user_script_slave.h"
     26 #include "chrome/renderer/web_apps.h"
     27 #include "content/public/renderer/render_view.h"
     28 #include "content/public/renderer/render_view_visitor.h"
     29 #include "extensions/common/constants.h"
     30 #include "third_party/WebKit/public/platform/WebURLRequest.h"
     31 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
     32 #include "third_party/WebKit/public/web/WebDocument.h"
     33 #include "third_party/WebKit/public/web/WebFrame.h"
     34 #include "third_party/WebKit/public/web/WebScopedUserGesture.h"
     35 #include "third_party/WebKit/public/web/WebView.h"
     36 
     37 using content::ConsoleMessageLevel;
     38 using blink::WebConsoleMessage;
     39 using blink::WebDataSource;
     40 using blink::WebFrame;
     41 using blink::WebURLRequest;
     42 using blink::WebScopedUserGesture;
     43 using blink::WebView;
     44 
     45 namespace extensions {
     46 
     47 namespace {
     48 // Keeps a mapping from the frame pointer to a UserScriptScheduler object.
     49 // We store this mapping per process, because a frame can jump from one
     50 // document to another with adoptNode, and so having the object be a
     51 // RenderViewObserver means it might miss some notifications after it moves.
     52 typedef std::map<WebFrame*, UserScriptScheduler*> SchedulerMap;
     53 static base::LazyInstance<SchedulerMap> g_schedulers =
     54     LAZY_INSTANCE_INITIALIZER;
     55 
     56 // A RenderViewVisitor class that iterates through the set of available
     57 // views, looking for a view of the given type, in the given browser window
     58 // and within the given extension.
     59 // Used to accumulate the list of views associated with an extension.
     60 class ViewAccumulator : public content::RenderViewVisitor {
     61  public:
     62   ViewAccumulator(const std::string& extension_id,
     63                   int browser_window_id,
     64                   ViewType view_type)
     65       : extension_id_(extension_id),
     66         browser_window_id_(browser_window_id),
     67         view_type_(view_type) {
     68   }
     69 
     70   std::vector<content::RenderView*> views() { return views_; }
     71 
     72   // Returns false to terminate the iteration.
     73   virtual bool Visit(content::RenderView* render_view) OVERRIDE {
     74     ExtensionHelper* helper = ExtensionHelper::Get(render_view);
     75     if (!ViewTypeMatches(helper->view_type(), view_type_))
     76       return true;
     77 
     78     GURL url = render_view->GetWebView()->mainFrame()->document().url();
     79     if (!url.SchemeIs(kExtensionScheme))
     80       return true;
     81     const std::string& extension_id = url.host();
     82     if (extension_id != extension_id_)
     83       return true;
     84 
     85     if (browser_window_id_ != extension_misc::kUnknownWindowId &&
     86         helper->browser_window_id() != browser_window_id_) {
     87       return true;
     88     }
     89 
     90     views_.push_back(render_view);
     91 
     92     if (view_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE)
     93       return false;  // There can be only one...
     94     return true;
     95   }
     96 
     97  private:
     98   // Returns true if |type| "isa" |match|.
     99   static bool ViewTypeMatches(ViewType type, ViewType match) {
    100     if (type == match)
    101       return true;
    102 
    103     // INVALID means match all.
    104     if (match == VIEW_TYPE_INVALID)
    105       return true;
    106 
    107     return false;
    108   }
    109 
    110   std::string extension_id_;
    111   int browser_window_id_;
    112   ViewType view_type_;
    113   std::vector<content::RenderView*> views_;
    114 };
    115 
    116 }  // namespace
    117 
    118 // static
    119 std::vector<content::RenderView*> ExtensionHelper::GetExtensionViews(
    120     const std::string& extension_id,
    121     int browser_window_id,
    122     ViewType view_type) {
    123   ViewAccumulator accumulator(extension_id, browser_window_id, view_type);
    124   content::RenderView::ForEach(&accumulator);
    125   return accumulator.views();
    126 }
    127 
    128 // static
    129 content::RenderView* ExtensionHelper::GetBackgroundPage(
    130     const std::string& extension_id) {
    131   ViewAccumulator accumulator(extension_id, extension_misc::kUnknownWindowId,
    132                               VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
    133   content::RenderView::ForEach(&accumulator);
    134   CHECK_LE(accumulator.views().size(), 1u);
    135   if (accumulator.views().size() == 0)
    136     return NULL;
    137   return accumulator.views()[0];
    138 }
    139 
    140 ExtensionHelper::ExtensionHelper(content::RenderView* render_view,
    141                                  Dispatcher* dispatcher)
    142     : content::RenderViewObserver(render_view),
    143       content::RenderViewObserverTracker<ExtensionHelper>(render_view),
    144       dispatcher_(dispatcher),
    145       pending_app_icon_requests_(0),
    146       view_type_(VIEW_TYPE_INVALID),
    147       tab_id_(-1),
    148       browser_window_id_(-1) {
    149 }
    150 
    151 ExtensionHelper::~ExtensionHelper() {
    152 }
    153 
    154 bool ExtensionHelper::OnMessageReceived(const IPC::Message& message) {
    155   bool handled = true;
    156   IPC_BEGIN_MESSAGE_MAP(ExtensionHelper, message)
    157     IPC_MESSAGE_HANDLER(ExtensionMsg_Response, OnExtensionResponse)
    158     IPC_MESSAGE_HANDLER(ExtensionMsg_MessageInvoke, OnExtensionMessageInvoke)
    159     IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnConnect,
    160                         OnExtensionDispatchOnConnect)
    161     IPC_MESSAGE_HANDLER(ExtensionMsg_DeliverMessage, OnExtensionDeliverMessage)
    162     IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnDisconnect,
    163                         OnExtensionDispatchOnDisconnect)
    164     IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteCode, OnExecuteCode)
    165     IPC_MESSAGE_HANDLER(ExtensionMsg_GetApplicationInfo, OnGetApplicationInfo)
    166     IPC_MESSAGE_HANDLER(ExtensionMsg_SetTabId, OnSetTabId)
    167     IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateBrowserWindowId,
    168                         OnUpdateBrowserWindowId)
    169     IPC_MESSAGE_HANDLER(ExtensionMsg_NotifyRenderViewType,
    170                         OnNotifyRendererViewType)
    171     IPC_MESSAGE_HANDLER(ExtensionMsg_AddMessageToConsole,
    172                         OnAddMessageToConsole)
    173     IPC_MESSAGE_HANDLER(ExtensionMsg_AppWindowClosed,
    174                         OnAppWindowClosed);
    175     IPC_MESSAGE_UNHANDLED(handled = false)
    176   IPC_END_MESSAGE_MAP()
    177   return handled;
    178 }
    179 
    180 void ExtensionHelper::DidFinishDocumentLoad(WebFrame* frame) {
    181   dispatcher_->user_script_slave()->InjectScripts(
    182       frame, UserScript::DOCUMENT_END);
    183 
    184   SchedulerMap::iterator i = g_schedulers.Get().find(frame);
    185   if (i != g_schedulers.Get().end())
    186     i->second->DidFinishDocumentLoad();
    187 }
    188 
    189 void ExtensionHelper::DidFinishLoad(blink::WebFrame* frame) {
    190   SchedulerMap::iterator i = g_schedulers.Get().find(frame);
    191   if (i != g_schedulers.Get().end())
    192     i->second->DidFinishLoad();
    193 }
    194 
    195 void ExtensionHelper::DidCreateDocumentElement(WebFrame* frame) {
    196   dispatcher_->user_script_slave()->InjectScripts(
    197       frame, UserScript::DOCUMENT_START);
    198   SchedulerMap::iterator i = g_schedulers.Get().find(frame);
    199   if (i != g_schedulers.Get().end())
    200     i->second->DidCreateDocumentElement();
    201 
    202   dispatcher_->DidCreateDocumentElement(frame);
    203 }
    204 
    205 void ExtensionHelper::DidStartProvisionalLoad(blink::WebFrame* frame) {
    206   SchedulerMap::iterator i = g_schedulers.Get().find(frame);
    207   if (i != g_schedulers.Get().end())
    208     i->second->DidStartProvisionalLoad();
    209 }
    210 
    211 void ExtensionHelper::DraggableRegionsChanged(blink::WebFrame* frame) {
    212   blink::WebVector<blink::WebDraggableRegion> webregions =
    213       frame->document().draggableRegions();
    214   std::vector<DraggableRegion> regions;
    215   for (size_t i = 0; i < webregions.size(); ++i) {
    216     DraggableRegion region;
    217     region.bounds = webregions[i].bounds;
    218     region.draggable = webregions[i].draggable;
    219     regions.push_back(region);
    220   }
    221   Send(new ExtensionHostMsg_UpdateDraggableRegions(routing_id(), regions));
    222 }
    223 
    224 void ExtensionHelper::FrameDetached(WebFrame* frame) {
    225   // This could be called before DidCreateDataSource, in which case the frame
    226   // won't be in the map.
    227   SchedulerMap::iterator i = g_schedulers.Get().find(frame);
    228   if (i == g_schedulers.Get().end())
    229     return;
    230 
    231   delete i->second;
    232   g_schedulers.Get().erase(i);
    233 }
    234 
    235 void ExtensionHelper::DidMatchCSS(
    236     blink::WebFrame* frame,
    237     const blink::WebVector<blink::WebString>& newly_matching_selectors,
    238     const blink::WebVector<blink::WebString>& stopped_matching_selectors) {
    239   dispatcher_->DidMatchCSS(
    240       frame, newly_matching_selectors, stopped_matching_selectors);
    241 }
    242 
    243 void ExtensionHelper::DidCreateDataSource(WebFrame* frame, WebDataSource* ds) {
    244   // Check first if we created a scheduler for the frame, since this function
    245   // gets called for navigations within the document.
    246   if (g_schedulers.Get().count(frame))
    247     return;
    248 
    249   g_schedulers.Get()[frame] = new UserScriptScheduler(frame, dispatcher_);
    250 }
    251 
    252 void ExtensionHelper::OnExtensionResponse(int request_id,
    253                                           bool success,
    254                                           const base::ListValue& response,
    255                                           const std::string& error) {
    256   dispatcher_->OnExtensionResponse(request_id,
    257                                    success,
    258                                    response,
    259                                    error);
    260 }
    261 
    262 void ExtensionHelper::OnExtensionMessageInvoke(const std::string& extension_id,
    263                                                const std::string& module_name,
    264                                                const std::string& function_name,
    265                                                const base::ListValue& args,
    266                                                bool user_gesture) {
    267   dispatcher_->InvokeModuleSystemMethod(
    268       render_view(), extension_id, module_name, function_name, args,
    269       user_gesture);
    270 }
    271 
    272 void ExtensionHelper::OnExtensionDispatchOnConnect(
    273     int target_port_id,
    274     const std::string& channel_name,
    275     const base::DictionaryValue& source_tab,
    276     const ExtensionMsg_ExternalConnectionInfo& info,
    277     const std::string& tls_channel_id) {
    278   MessagingBindings::DispatchOnConnect(
    279       dispatcher_->v8_context_set().GetAll(),
    280       target_port_id, channel_name, source_tab,
    281       info.source_id, info.target_id, info.source_url,
    282       tls_channel_id, render_view());
    283 }
    284 
    285 void ExtensionHelper::OnExtensionDeliverMessage(int target_id,
    286                                                 const Message& message) {
    287   MessagingBindings::DeliverMessage(dispatcher_->v8_context_set().GetAll(),
    288                                         target_id,
    289                                         message,
    290                                         render_view());
    291 }
    292 
    293 void ExtensionHelper::OnExtensionDispatchOnDisconnect(
    294     int port_id,
    295     const std::string& error_message) {
    296   MessagingBindings::DispatchOnDisconnect(
    297       dispatcher_->v8_context_set().GetAll(),
    298       port_id, error_message,
    299       render_view());
    300 }
    301 
    302 void ExtensionHelper::OnExecuteCode(
    303     const ExtensionMsg_ExecuteCode_Params& params) {
    304   WebView* webview = render_view()->GetWebView();
    305   WebFrame* main_frame = webview->mainFrame();
    306   if (!main_frame) {
    307     ListValue val;
    308     Send(new ExtensionHostMsg_ExecuteCodeFinished(routing_id(),
    309                                                   params.request_id,
    310                                                   "No main frame",
    311                                                   -1,
    312                                                   GURL(std::string()),
    313                                                   val));
    314     return;
    315   }
    316 
    317   // chrome.tabs.executeScript() only supports execution in either the top frame
    318   // or all frames.  We handle both cases in the top frame.
    319   SchedulerMap::iterator i = g_schedulers.Get().find(main_frame);
    320   if (i != g_schedulers.Get().end())
    321     i->second->ExecuteCode(params);
    322 }
    323 
    324 void ExtensionHelper::OnGetApplicationInfo(int page_id) {
    325   WebApplicationInfo app_info;
    326   if (page_id == render_view()->GetPageId()) {
    327     base::string16 error;
    328     web_apps::ParseWebAppFromWebDocument(
    329         render_view()->GetWebView()->mainFrame(), &app_info, &error);
    330   }
    331 
    332   // Prune out any data URLs in the set of icons.  The browser process expects
    333   // any icon with a data URL to have originated from a favicon.  We don't want
    334   // to decode arbitrary data URLs in the browser process.  See
    335   // http://b/issue?id=1162972
    336   for (size_t i = 0; i < app_info.icons.size(); ++i) {
    337     if (app_info.icons[i].url.SchemeIs(chrome::kDataScheme)) {
    338       app_info.icons.erase(app_info.icons.begin() + i);
    339       --i;
    340     }
    341   }
    342 
    343   Send(new ExtensionHostMsg_DidGetApplicationInfo(
    344       routing_id(), page_id, app_info));
    345 }
    346 
    347 void ExtensionHelper::OnNotifyRendererViewType(ViewType type) {
    348   view_type_ = type;
    349 }
    350 
    351 void ExtensionHelper::OnSetTabId(int init_tab_id) {
    352   CHECK_EQ(tab_id_, -1);
    353   CHECK_GE(init_tab_id, 0);
    354   tab_id_ = init_tab_id;
    355 }
    356 
    357 void ExtensionHelper::OnUpdateBrowserWindowId(int window_id) {
    358   browser_window_id_ = window_id;
    359 }
    360 
    361 void ExtensionHelper::OnAddMessageToConsole(ConsoleMessageLevel level,
    362                                             const std::string& message) {
    363   console::AddMessage(render_view(), level, message);
    364 }
    365 
    366 void ExtensionHelper::OnAppWindowClosed() {
    367   v8::HandleScope scope(v8::Isolate::GetCurrent());
    368   v8::Handle<v8::Context> script_context =
    369       render_view()->GetWebView()->mainFrame()->mainWorldScriptContext();
    370   ChromeV8Context* chrome_v8_context =
    371       dispatcher_->v8_context_set().GetByV8Context(script_context);
    372   if (!chrome_v8_context)
    373     return;
    374   chrome_v8_context->module_system()->CallModuleMethod(
    375       "app.window", "onAppWindowClosed");
    376 }
    377 
    378 }  // namespace extensions
    379