// 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& new_css_selectors_utf8) { blink::WebVector 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& css_selectors) : css_selectors_(css_selectors) {} virtual 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& 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& newly_matching_selectors, const WebVector& stopped_matching_selectors) { std::set& 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 transitive_selectors; for (blink::WebFrame* frame = top_frame; frame; frame = frame->traverseNext(/*wrap=*/false)) { if (top_origin.canAccess(frame->document().securityOrigin())) { std::map >::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 selector_strings; for (std::set::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