Home | History | Annotate | Download | only in renderer
      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/content_watcher.h"
      6 
      7 #include "content/public/renderer/render_view.h"
      8 #include "content/public/renderer/render_view_visitor.h"
      9 #include "extensions/common/extension_messages.h"
     10 #include "third_party/WebKit/public/web/WebDocument.h"
     11 #include "third_party/WebKit/public/web/WebElement.h"
     12 #include "third_party/WebKit/public/web/WebFrame.h"
     13 #include "third_party/WebKit/public/web/WebScriptBindings.h"
     14 #include "third_party/WebKit/public/web/WebView.h"
     15 
     16 namespace extensions {
     17 
     18 using blink::WebString;
     19 using blink::WebVector;
     20 using blink::WebView;
     21 
     22 ContentWatcher::ContentWatcher() {}
     23 ContentWatcher::~ContentWatcher() {}
     24 
     25 void ContentWatcher::OnWatchPages(
     26     const std::vector<std::string>& new_css_selectors_utf8) {
     27   blink::WebVector<blink::WebString> new_css_selectors(
     28       new_css_selectors_utf8.size());
     29   bool changed = new_css_selectors.size() != css_selectors_.size();
     30   for (size_t i = 0; i < new_css_selectors.size(); ++i) {
     31     new_css_selectors[i] =
     32         blink::WebString::fromUTF8(new_css_selectors_utf8[i]);
     33     if (!changed && new_css_selectors[i] != css_selectors_[i])
     34       changed = true;
     35   }
     36 
     37   if (!changed)
     38     return;
     39 
     40   css_selectors_.swap(new_css_selectors);
     41 
     42   // Tell each frame's document about the new set of watched selectors. These
     43   // will trigger calls to DidMatchCSS after Blink has a chance to apply the new
     44   // style, which will in turn notify the browser about the changes.
     45   struct WatchSelectors : public content::RenderViewVisitor {
     46     explicit WatchSelectors(const WebVector<WebString>& css_selectors)
     47         : css_selectors_(css_selectors) {}
     48 
     49     virtual bool Visit(content::RenderView* view) OVERRIDE {
     50       for (blink::WebFrame* frame = view->GetWebView()->mainFrame(); frame;
     51            frame = frame->traverseNext(/*wrap=*/false))
     52         frame->document().watchCSSSelectors(css_selectors_);
     53 
     54       return true;  // Continue visiting.
     55     }
     56 
     57     const WebVector<WebString>& css_selectors_;
     58   };
     59   WatchSelectors visitor(css_selectors_);
     60   content::RenderView::ForEach(&visitor);
     61 }
     62 
     63 void ContentWatcher::DidCreateDocumentElement(blink::WebFrame* frame) {
     64   frame->document().watchCSSSelectors(css_selectors_);
     65 }
     66 
     67 void ContentWatcher::DidMatchCSS(
     68     blink::WebFrame* frame,
     69     const WebVector<WebString>& newly_matching_selectors,
     70     const WebVector<WebString>& stopped_matching_selectors) {
     71   std::set<std::string>& frame_selectors = matching_selectors_[frame];
     72   for (size_t i = 0; i < stopped_matching_selectors.size(); ++i)
     73     frame_selectors.erase(stopped_matching_selectors[i].utf8());
     74   for (size_t i = 0; i < newly_matching_selectors.size(); ++i)
     75     frame_selectors.insert(newly_matching_selectors[i].utf8());
     76 
     77   if (frame_selectors.empty())
     78     matching_selectors_.erase(frame);
     79 
     80   NotifyBrowserOfChange(frame);
     81 }
     82 
     83 void ContentWatcher::NotifyBrowserOfChange(
     84     blink::WebFrame* changed_frame) const {
     85   blink::WebFrame* const top_frame = changed_frame->top();
     86   const blink::WebSecurityOrigin top_origin =
     87       top_frame->document().securityOrigin();
     88   // Want to aggregate matched selectors from all frames where an
     89   // extension with access to top_origin could run on the frame.
     90   if (!top_origin.canAccess(changed_frame->document().securityOrigin())) {
     91     // If the changed frame can't be accessed by the top frame, then
     92     // no change in it could affect the set of selectors we'd send back.
     93     return;
     94   }
     95 
     96   std::set<base::StringPiece> transitive_selectors;
     97   for (blink::WebFrame* frame = top_frame; frame;
     98        frame = frame->traverseNext(/*wrap=*/false)) {
     99     if (top_origin.canAccess(frame->document().securityOrigin())) {
    100       std::map<blink::WebFrame*, std::set<std::string> >::const_iterator
    101           frame_selectors = matching_selectors_.find(frame);
    102       if (frame_selectors != matching_selectors_.end()) {
    103         transitive_selectors.insert(frame_selectors->second.begin(),
    104                                     frame_selectors->second.end());
    105       }
    106     }
    107   }
    108   std::vector<std::string> selector_strings;
    109   for (std::set<base::StringPiece>::const_iterator it =
    110            transitive_selectors.begin();
    111        it != transitive_selectors.end();
    112        ++it)
    113     selector_strings.push_back(it->as_string());
    114   content::RenderView* view =
    115       content::RenderView::FromWebView(top_frame->view());
    116   view->Send(new ExtensionHostMsg_OnWatchedPageChange(view->GetRoutingID(),
    117                                                       selector_strings));
    118 }
    119 
    120 }  // namespace extensions
    121