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