blob: eea0d561424e502db950cf80e08d038ab670b4bb [file] [log] [blame]
// Copyright 2016 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 "third_party/blink/public/platform/web_document_subresource_filter.h"
#include <string>
#include <vector>
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_cache.h"
#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_element.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/html/html_image_element.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
namespace blink {
namespace {
class TestDocumentSubresourceFilter : public WebDocumentSubresourceFilter {
public:
explicit TestDocumentSubresourceFilter(LoadPolicy policy)
: load_policy_(policy) {}
LoadPolicy GetLoadPolicy(const WebURL& resource_url,
mojom::RequestContextType) override {
std::string resource_path = WebString(KURL(resource_url).GetPath()).Utf8();
if (std::find(queried_subresource_paths_.begin(),
queried_subresource_paths_.end(),
resource_path) == queried_subresource_paths_.end()) {
queried_subresource_paths_.push_back(resource_path);
}
String resource_string = resource_url.GetString();
for (const String& suffix : blacklisted_suffixes_) {
if (resource_string.EndsWith(suffix))
return load_policy_;
}
return LoadPolicy::kAllow;
}
LoadPolicy GetLoadPolicyForWebSocketConnect(const WebURL& url) override {
return kAllow;
}
void ReportDisallowedLoad() override {}
bool ShouldLogToConsole() override { return false; }
void AddToBlacklist(const String& suffix) {
blacklisted_suffixes_.push_back(suffix);
}
const std::vector<std::string>& QueriedSubresourcePaths() const {
return queried_subresource_paths_;
}
private:
// Using STL types for compatibility with gtest/gmock.
std::vector<std::string> queried_subresource_paths_;
Vector<String> blacklisted_suffixes_;
LoadPolicy load_policy_;
};
class SubresourceFilteringWebFrameClient
: public frame_test_helpers::TestWebFrameClient {
public:
void DidStartProvisionalLoad(WebDocumentLoader* data_source) override {
// Normally, the filter should be set when the load is committed. For
// the sake of this test, however, inject it earlier to verify that it
// is not consulted for the main resource load.
subresource_filter_ =
new TestDocumentSubresourceFilter(load_policy_for_next_load_);
subresource_filter_->AddToBlacklist("1x1.png");
data_source->SetSubresourceFilter(subresource_filter_);
}
void SetLoadPolicyFromNextLoad(
TestDocumentSubresourceFilter::LoadPolicy policy) {
load_policy_for_next_load_ = policy;
}
const TestDocumentSubresourceFilter* SubresourceFilter() const {
return subresource_filter_;
}
private:
// Weak, owned by WebDocumentLoader.
TestDocumentSubresourceFilter* subresource_filter_ = nullptr;
TestDocumentSubresourceFilter::LoadPolicy load_policy_for_next_load_;
};
} // namespace
class WebDocumentSubresourceFilterTest : public testing::Test {
protected:
WebDocumentSubresourceFilterTest() : base_url_("http://internal.test/") {
RegisterMockedHttpURLLoad("white-1x1.png");
RegisterMockedHttpURLLoad("foo_with_image.html");
web_view_helper_.Initialize(&client_);
}
void LoadDocument(TestDocumentSubresourceFilter::LoadPolicy policy) {
client_.SetLoadPolicyFromNextLoad(policy);
frame_test_helpers::LoadFrame(MainFrame(),
BaseURL() + "foo_with_image.html");
}
void ExpectSubresourceWasLoaded(bool loaded) {
WebElement web_element = MainFrame()->GetDocument().QuerySelector("img");
HTMLImageElement* image_element = ToHTMLImageElement(web_element);
EXPECT_EQ(loaded, !!image_element->naturalWidth());
}
const std::string& BaseURL() const { return base_url_; }
WebLocalFrameImpl* MainFrame() { return web_view_helper_.LocalMainFrame(); }
const std::vector<std::string>& QueriedSubresourcePaths() const {
return client_.SubresourceFilter()->QueriedSubresourcePaths();
}
private:
void RegisterMockedHttpURLLoad(const std::string& file_name) {
url_test_helpers::RegisterMockedURLLoadFromBase(
WebString::FromUTF8(base_url_), test::CoreTestDataPath(),
WebString::FromUTF8(file_name));
}
// testing::Test:
void TearDown() override {
Platform::Current()
->GetURLLoaderMockFactory()
->UnregisterAllURLsAndClearMemoryCache();
}
SubresourceFilteringWebFrameClient client_;
frame_test_helpers::WebViewHelper web_view_helper_;
std::string base_url_;
};
TEST_F(WebDocumentSubresourceFilterTest, AllowedSubresource) {
LoadDocument(TestDocumentSubresourceFilter::kAllow);
ExpectSubresourceWasLoaded(true);
// The filter should not be consulted for the main document resource.
EXPECT_THAT(QueriedSubresourcePaths(),
testing::ElementsAre("/white-1x1.png"));
}
TEST_F(WebDocumentSubresourceFilterTest, DisallowedSubresource) {
LoadDocument(TestDocumentSubresourceFilter::kDisallow);
ExpectSubresourceWasLoaded(false);
}
TEST_F(WebDocumentSubresourceFilterTest, FilteringDecisionIsMadeLoadByLoad) {
for (const TestDocumentSubresourceFilter::LoadPolicy policy :
{TestDocumentSubresourceFilter::kDisallow,
TestDocumentSubresourceFilter::kAllow,
TestDocumentSubresourceFilter::kWouldDisallow}) {
SCOPED_TRACE(testing::Message() << "First load policy= " << policy);
LoadDocument(policy);
ExpectSubresourceWasLoaded(policy !=
TestDocumentSubresourceFilter::kDisallow);
EXPECT_THAT(QueriedSubresourcePaths(),
testing::ElementsAre("/white-1x1.png"));
WebCache::Clear();
}
}
} // namespace blink