user_script_listener.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2009 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/browser/extensions/user_script_listener.h"
6
7#include "chrome/browser/browser_thread.h"
8#include "chrome/browser/extensions/extensions_service.h"
9#include "chrome/browser/profile.h"
10#include "chrome/browser/renderer_host/global_request_id.h"
11#include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h"
12#include "chrome/common/extensions/extension.h"
13#include "chrome/common/extensions/url_pattern.h"
14#include "chrome/common/notification_service.h"
15#include "net/url_request/url_request.h"
16
17UserScriptListener::UserScriptListener(ResourceQueue* resource_queue)
18    : resource_queue_(resource_queue),
19      user_scripts_ready_(false) {
20  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
21  DCHECK(resource_queue_);
22
23  registrar_.Add(this, NotificationType::EXTENSION_LOADED,
24                 NotificationService::AllSources());
25  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
26                 NotificationService::AllSources());
27  registrar_.Add(this, NotificationType::USER_SCRIPTS_UPDATED,
28                 NotificationService::AllSources());
29}
30
31void UserScriptListener::ShutdownMainThread() {
32  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
33  registrar_.RemoveAll();
34}
35
36bool UserScriptListener::ShouldDelayRequest(
37    URLRequest* request,
38    const ResourceDispatcherHostRequestInfo& request_info,
39    const GlobalRequestID& request_id) {
40  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
41
42  // If it's a frame load, then we need to check the URL against the list of
43  // user scripts to see if we need to wait.
44  if (request_info.resource_type() != ResourceType::MAIN_FRAME &&
45      request_info.resource_type() != ResourceType::SUB_FRAME) {
46    return false;
47  }
48
49  if (user_scripts_ready_)
50    return false;
51
52  for (URLPatterns::iterator it = url_patterns_.begin();
53       it != url_patterns_.end(); ++it) {
54    if ((*it).MatchesUrl(request->url())) {
55      // One of the user scripts wants to inject into this request, but the
56      // script isn't ready yet. Delay the request.
57      delayed_request_ids_.push_front(request_id);
58      return true;
59    }
60  }
61
62  return false;
63}
64
65void UserScriptListener::WillShutdownResourceQueue() {
66  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
67  resource_queue_ = NULL;
68}
69
70UserScriptListener::~UserScriptListener() {
71}
72
73void UserScriptListener::StartDelayedRequests() {
74  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
75
76  user_scripts_ready_ = true;
77
78  if (resource_queue_) {
79    for (DelayedRequests::iterator it = delayed_request_ids_.begin();
80         it != delayed_request_ids_.end(); ++it) {
81      resource_queue_->StartDelayedRequest(this, *it);
82    }
83  }
84
85  delayed_request_ids_.clear();
86}
87
88void UserScriptListener::AppendNewURLPatterns(const URLPatterns& new_patterns) {
89  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
90
91  user_scripts_ready_ = false;
92  url_patterns_.insert(url_patterns_.end(),
93                       new_patterns.begin(), new_patterns.end());
94}
95
96void UserScriptListener::ReplaceURLPatterns(const URLPatterns& patterns) {
97  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
98  url_patterns_ = patterns;
99}
100
101void UserScriptListener::CollectURLPatterns(const Extension* extension,
102                                            URLPatterns* patterns) {
103  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
104
105  const UserScriptList& scripts = extension->content_scripts();
106  for (UserScriptList::const_iterator iter = scripts.begin();
107       iter != scripts.end(); ++iter) {
108    patterns->insert(patterns->end(),
109                     (*iter).url_patterns().begin(),
110                     (*iter).url_patterns().end());
111  }
112}
113
114void UserScriptListener::Observe(NotificationType type,
115                                 const NotificationSource& source,
116                                 const NotificationDetails& details) {
117  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
118
119  switch (type.value) {
120    case NotificationType::EXTENSION_LOADED: {
121      const Extension* extension = Details<const Extension>(details).ptr();
122      if (extension->content_scripts().empty())
123        return;  // no new patterns from this extension.
124
125      URLPatterns new_patterns;
126      CollectURLPatterns(Details<const Extension>(details).ptr(),
127                         &new_patterns);
128      if (!new_patterns.empty()) {
129        BrowserThread::PostTask(
130            BrowserThread::IO, FROM_HERE,
131            NewRunnableMethod(
132                this, &UserScriptListener::AppendNewURLPatterns, new_patterns));
133      }
134      break;
135    }
136
137    case NotificationType::EXTENSION_UNLOADED: {
138      const Extension* unloaded_extension =
139          Details<const Extension>(details).ptr();
140      if (unloaded_extension->content_scripts().empty())
141        return;  // no patterns to delete for this extension.
142
143      // Clear all our patterns and reregister all the still-loaded extensions.
144      URLPatterns new_patterns;
145      ExtensionsService* service =
146          Source<Profile>(source).ptr()->GetExtensionsService();
147      for (ExtensionList::const_iterator it = service->extensions()->begin();
148           it != service->extensions()->end(); ++it) {
149        if (*it != unloaded_extension)
150          CollectURLPatterns(*it, &new_patterns);
151      }
152      BrowserThread::PostTask(
153          BrowserThread::IO, FROM_HERE,
154          NewRunnableMethod(
155              this, &UserScriptListener::ReplaceURLPatterns, new_patterns));
156      break;
157    }
158
159    case NotificationType::USER_SCRIPTS_UPDATED: {
160      BrowserThread::PostTask(
161          BrowserThread::IO, FROM_HERE,
162          NewRunnableMethod(this, &UserScriptListener::StartDelayedRequests));
163      break;
164    }
165
166    default:
167      NOTREACHED();
168  }
169}
170