summaryrefslogtreecommitdiffstats
path: root/extensions/renderer/content_watcher.cc
blob: ffc1c86d3d6d9502f027910be1b2df5c61a96869 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/renderer/content_watcher.h"

#include "content/public/renderer/render_view.h"
#include "content/public/renderer/render_view_visitor.h"
#include "extensions/common/extension_messages.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebElement.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebScriptBindings.h"
#include "third_party/WebKit/public/web/WebView.h"

namespace extensions {

using blink::WebString;
using blink::WebVector;
using blink::WebView;

ContentWatcher::ContentWatcher() {}
ContentWatcher::~ContentWatcher() {}

void ContentWatcher::OnWatchPages(
    const std::vector<std::string>& new_css_selectors_utf8) {
  blink::WebVector<blink::WebString> new_css_selectors(
      new_css_selectors_utf8.size());
  bool changed = new_css_selectors.size() != css_selectors_.size();
  for (size_t i = 0; i < new_css_selectors.size(); ++i) {
    new_css_selectors[i] =
        blink::WebString::fromUTF8(new_css_selectors_utf8[i]);
    if (!changed && new_css_selectors[i] != css_selectors_[i])
      changed = true;
  }

  if (!changed)
    return;

  css_selectors_.swap(new_css_selectors);

  // Tell each frame's document about the new set of watched selectors. These
  // will trigger calls to DidMatchCSS after Blink has a chance to apply the new
  // style, which will in turn notify the browser about the changes.
  struct WatchSelectors : public content::RenderViewVisitor {
    explicit WatchSelectors(const WebVector<WebString>& css_selectors)
        : css_selectors_(css_selectors) {}

    bool Visit(content::RenderView* view) override {
      for (blink::WebFrame* frame = view->GetWebView()->mainFrame(); frame;
           frame = frame->traverseNext(/*wrap=*/false))
        frame->document().watchCSSSelectors(css_selectors_);

      return true;  // Continue visiting.
    }

    const WebVector<WebString>& css_selectors_;
  };
  WatchSelectors visitor(css_selectors_);
  content::RenderView::ForEach(&visitor);
}

void ContentWatcher::DidCreateDocumentElement(blink::WebFrame* frame) {
  frame->document().watchCSSSelectors(css_selectors_);
}

void ContentWatcher::DidMatchCSS(
    blink::WebFrame* frame,
    const WebVector<WebString>& newly_matching_selectors,
    const WebVector<WebString>& stopped_matching_selectors) {
  std::set<std::string>& frame_selectors = matching_selectors_[frame];
  for (size_t i = 0; i < stopped_matching_selectors.size(); ++i)
    frame_selectors.erase(stopped_matching_selectors[i].utf8());
  for (size_t i = 0; i < newly_matching_selectors.size(); ++i)
    frame_selectors.insert(newly_matching_selectors[i].utf8());

  if (frame_selectors.empty())
    matching_selectors_.erase(frame);

  NotifyBrowserOfChange(frame);
}

void ContentWatcher::NotifyBrowserOfChange(
    blink::WebFrame* changed_frame) const {
  blink::WebFrame* const top_frame = changed_frame->top();
  const blink::WebSecurityOrigin top_origin =
      top_frame->document().securityOrigin();
  // Want to aggregate matched selectors from all frames where an
  // extension with access to top_origin could run on the frame.
  if (!top_origin.canAccess(changed_frame->document().securityOrigin())) {
    // If the changed frame can't be accessed by the top frame, then
    // no change in it could affect the set of selectors we'd send back.
    return;
  }

  std::set<base::StringPiece> transitive_selectors;
  for (blink::WebFrame* frame = top_frame; frame;
       frame = frame->traverseNext(/*wrap=*/false)) {
    if (top_origin.canAccess(frame->document().securityOrigin())) {
      std::map<blink::WebFrame*, std::set<std::string> >::const_iterator
          frame_selectors = matching_selectors_.find(frame);
      if (frame_selectors != matching_selectors_.end()) {
        transitive_selectors.insert(frame_selectors->second.begin(),
                                    frame_selectors->second.end());
      }
    }
  }
  std::vector<std::string> selector_strings;
  for (std::set<base::StringPiece>::const_iterator it =
           transitive_selectors.begin();
       it != transitive_selectors.end();
       ++it)
    selector_strings.push_back(it->as_string());
  content::RenderView* view =
      content::RenderView::FromWebView(top_frame->view());
  view->Send(new ExtensionHostMsg_OnWatchedPageChange(view->GetRoutingID(),
                                                      selector_strings));
}

}  // namespace extensions