blob: 1b13b8d06dc72f68db95138f92d515b02b95c66d [file] [log] [blame]
// Copyright 2013 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 "chrome/browser/search/most_visited_iframe_source.h"
#include <memory>
#include "base/bind.h"
#include "base/memory/ref_counted_memory.h"
#include "chrome/browser/search/instant_io_context.h"
#include "chrome/grit/local_ntp_resources.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/previews_state.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/mock_resource_context.h"
#include "ipc/ipc_message.h"
#include "net/base/request_priority.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
const int kNonInstantRendererPID = 0;
const char kNonInstantOrigin[] = "http://evil";
const int kInstantRendererPID = 1;
const char kInstantOrigin[] = "chrome-search://instant";
const int kInvalidRendererPID = 42;
class TestMostVisitedIframeSource : public MostVisitedIframeSource {
public:
using MostVisitedIframeSource::GetMimeType;
using MostVisitedIframeSource::SendJSWithOrigin;
using MostVisitedIframeSource::SendResource;
using MostVisitedIframeSource::ShouldServiceRequest;
void set_origin(std::string origin) { origin_ = origin; }
protected:
std::string GetSource() override { return "test"; }
bool ServesPath(const std::string& path) const override {
return path == "/valid.html" || path == "/valid.js";
}
void StartDataRequest(
const GURL& url,
const content::WebContents::Getter& wc_getter,
const content::URLDataSource::GotDataCallback& callback) override {}
// RenderFrameHost is hard to mock in concert with everything else, so stub
// this method out for testing.
bool GetOrigin(const content::WebContents::Getter& wc_getter,
std::string* origin) const override {
if (origin_.empty())
return false;
*origin = origin_;
return true;
}
private:
std::string origin_;
};
class MostVisitedIframeSourceTest : public testing::Test {
public:
// net::URLRequest wants to be executed with a message loop that has TYPE_IO.
// InstantIOContext needs to be created on the UI thread and have everything
// else happen on the IO thread. This setup is a hacky way to satisfy all
// those constraints.
MostVisitedIframeSourceTest()
: task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP),
instant_io_context_(nullptr),
response_(nullptr) {}
TestMostVisitedIframeSource* source() { return source_.get(); }
std::string response_string() {
if (response_.get()) {
return std::string(response_->front_as<char>(), response_->size());
}
return "";
}
void SendResource(int resource_id) {
source()->SendResource(resource_id, callback_);
}
void SendJSWithOrigin(int resource_id) {
source()->SendJSWithOrigin(resource_id, content::WebContents::Getter(),
callback_);
}
bool ShouldService(const std::string& path, int process_id) {
return source()->ShouldServiceRequest(GURL(path), &resource_context_,
process_id);
}
private:
void SetUp() override {
source_ = std::make_unique<TestMostVisitedIframeSource>();
callback_ = base::Bind(&MostVisitedIframeSourceTest::SaveResponse,
base::Unretained(this));
instant_io_context_ = new InstantIOContext;
InstantIOContext::SetUserDataOnIO(&resource_context_, instant_io_context_);
source_->set_origin(kInstantOrigin);
InstantIOContext::AddInstantProcessOnIO(instant_io_context_,
kInstantRendererPID);
response_ = nullptr;
}
void TearDown() override { source_.reset(); }
void SaveResponse(scoped_refptr<base::RefCountedMemory> data) {
response_ = data;
}
content::BrowserTaskEnvironment task_environment_;
net::TestURLRequestContext test_url_request_context_;
content::MockResourceContext resource_context_;
std::unique_ptr<TestMostVisitedIframeSource> source_;
content::URLDataSource::GotDataCallback callback_;
scoped_refptr<InstantIOContext> instant_io_context_;
scoped_refptr<base::RefCountedMemory> response_;
};
TEST_F(MostVisitedIframeSourceTest, ShouldServiceRequest) {
source()->set_origin(kNonInstantOrigin);
EXPECT_FALSE(ShouldService("http://test/loader.js", kNonInstantRendererPID));
source()->set_origin(kInstantOrigin);
EXPECT_FALSE(
ShouldService("chrome-search://bogus/valid.js", kInstantRendererPID));
source()->set_origin(kInstantOrigin);
EXPECT_FALSE(
ShouldService("chrome-search://test/bogus.js", kInstantRendererPID));
source()->set_origin(kInstantOrigin);
EXPECT_TRUE(
ShouldService("chrome-search://test/valid.js", kInstantRendererPID));
source()->set_origin(kNonInstantOrigin);
EXPECT_FALSE(
ShouldService("chrome-search://test/valid.js", kNonInstantRendererPID));
source()->set_origin(std::string());
EXPECT_FALSE(
ShouldService("chrome-search://test/valid.js", kInvalidRendererPID));
}
TEST_F(MostVisitedIframeSourceTest, GetMimeType) {
// URLDataManagerBackend does not include / in path_and_query.
EXPECT_EQ("text/html", source()->GetMimeType("foo.html"));
EXPECT_EQ("application/javascript", source()->GetMimeType("foo.js"));
EXPECT_EQ("text/css", source()->GetMimeType("foo.css"));
EXPECT_EQ("image/png", source()->GetMimeType("foo.png"));
EXPECT_EQ("", source()->GetMimeType("bogus"));
}
TEST_F(MostVisitedIframeSourceTest, SendResource) {
SendResource(IDR_MOST_VISITED_TITLE_HTML);
EXPECT_FALSE(response_string().empty());
}
TEST_F(MostVisitedIframeSourceTest, SendJSWithOrigin) {
source()->set_origin(kInstantOrigin);
SendJSWithOrigin(IDR_MOST_VISITED_TITLE_JS);
EXPECT_FALSE(response_string().empty());
source()->set_origin(kNonInstantOrigin);
SendJSWithOrigin(IDR_MOST_VISITED_TITLE_JS);
EXPECT_FALSE(response_string().empty());
source()->set_origin(std::string());
SendJSWithOrigin(IDR_MOST_VISITED_TITLE_JS);
EXPECT_TRUE(response_string().empty());
}