cef/tests/ceftests/plugin_unittest.cc

614 lines
20 KiB
C++
Raw Normal View History

// Copyright (c) 2015 The Chromium Embedded Framework 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 "include/base/cef_bind.h"
#include "include/cef_pack_resources.h"
Implement NetworkService request interception/handling (see issue #2622). Implementation notes: - Chromium change: CookieMonster::SetCookieableSchemes needs to be called immediately after the CookieMonster is created in NetworkContext:: ApplyContextParamsToBuilder. Add a Profile::GetCookieableSchemes method and NetworkContextParams.cookieable_schemes member (set from ProfileNetworkContextService::CreateNetworkContextParams) to support that. - Chromium change: Add a ContentBrowserClient::HandleExternalProtocol variant that exposes additional NetworkService request information. - GetResourceResponseFilter is not yet implemented. API changes: - Resource-related callbacks have been moved from CefRequestHandler to a new CefResourceRequestHandler interface which is returned via the GetResourceRequestHandler method. If the CefRequestHandler declines to handle a resource it can optionally be handled by the CefRequestContextHandler, if any, associated with the loading context. - The OnProtocolExecution callback has been moved from CefRequestHandler to CefResourceRequestHandler and will be called if a custom scheme request is unhandled. - Cookie send/save permission callbacks have been moved from CefRequestHandler and CefResourceHandler to CefResourceRequestHandler. - New methods added to CefResourceHandler that better match NetworkService execution sequence expectations. The old methods are now deprecated. - New methods added to CefRequest and CefResponse. Known behavior changes with the NetworkService implementation: - Modifying the |new_url| parameter in OnResourceRedirect will no longer result in the method being called an additional time (likely a bug in the old implementation). - Modifying the request URL in OnResourceResponse would previously cause a redirect. This behavior is now deprecated because the NetworkService does not support this functionality when using default network loaders. Temporary support has been added in combination with CefResourceHandler usage only. - Other changes to the request object in OnResourceResponse will now cause the request to be restarted. This means that OnBeforeResourceLoad, etc, will be called an additional time with the new request information. - CefResponse::GetMimeType will now be empty for non-200 responses. - Requests using custom schemes can now be handled via CefResourceRequestHandler with the same callback behavior as builtin schemes. - Redirects of custom scheme requests will now be followed as expected. - Default handling of builtin schemes can now be disabled by setting |disable_default_handling| to true in GetResourceRequestHandler. - Unhandled requests (custom scheme or builtin scheme with default handling disabled) will fail with an CefResponse::GetError value of ERR_UNKNOWN_URL_SCHEME. - The CefSchemeHandlerFactory::Create callback will now include cookie headers. To test: - Run `cefclient --enable-network-service`. All resources should load successfully (this tests the transparent proxy capability). - All tests pass with NetworkService disabled. - The following tests pass with NetworkService enabled: - CookieTest.* - FrameTest.* (excluding .*Nav) - NavigationTest.* (excluding .Redirect*) - RequestHandlerTest.* - RequestContextTest.Basic* - RequestContextTest.Popup* - RequestTest.* - ResourceManagerTest.* - ResourceRequestHandlerTest.* (excluding .Filter*) - SchemeHandlerTest.* - StreamResourceHandlerTest.*
2019-04-24 04:50:25 +02:00
#include "include/cef_request_context_handler.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_stream_resource_handler.h"
#include "tests/ceftests/routing_test_handler.h"
#include "tests/gtest/include/gtest/gtest.h"
#include "tests/shared/browser/client_app_browser.h"
#include "tests/shared/browser/resource_util.h"
namespace {
// Browser-side app delegate.
class PluginBrowserTest : public client::ClientAppBrowser::Delegate {
public:
PluginBrowserTest() {}
void OnBeforeCommandLineProcessing(
CefRefPtr<client::ClientAppBrowser> app,
CefRefPtr<CefCommandLine> command_line) override {
// Allow all plugin loading by default.
command_line->AppendSwitchWithValue("plugin-policy", "allow");
}
private:
IMPLEMENT_REFCOUNTING(PluginBrowserTest);
};
const char kPdfTestOrigin[] = "http://tests/";
const char kPdfHtmlUrl[] = "http://tests/pdf.html";
const char kPdfDirectUrl[] = "http://tests/pdf.pdf";
// Delay waiting for iframe tests to load the PDF file.
#if defined(OS_LINUX)
const int64 kPdfLoadDelayMs = 7000;
#else
const int64 kPdfLoadDelayMs = 5000;
#endif
// Browser-side test handler.
class PluginTestHandler : public RoutingTestHandler,
public CefContextMenuHandler {
public:
enum Mode {
// No specified context or handler (implicitly uses the global context).
GLOBAL_DEFAULT,
// Global context with no handler.
GLOBAL_NO_HANDLER,
// Global context with handler that allows plugin load.
GLOBAL_ALLOW,
// Global context with handler that blocks plugin load. Then, load the
// plugin via the context menu.
GLOBAL_BLOCK_LOAD,
// Global context with handler that blocks plugin load. Then, hide the
// plugin via the context menu.
GLOBAL_BLOCK_HIDE,
// Global context with handler that disables plugin load. Then, hide the
// plugin via the context menu.
GLOBAL_DISABLE_HIDE,
// Global context with handler that removes the plugin from the
// `navigator.plugins` list and consequently disables plugin load.
GLOBAL_NO_LIST,
// Custom context with no handler.
CUSTOM_NO_HANDLER,
// Custom context with handler that allows plugin load.
CUSTOM_ALLOW,
// Custom context with handler that blocks plugin load. Then, load the
// plugin via the context menu.
CUSTOM_BLOCK_LOAD,
// Custom context with handler that blocks plugin load. Then, hide the
// plugin via the context menu.
CUSTOM_BLOCK_HIDE,
// Custom context with handler that disables plugin load. Then, hide the
// plugin via the context menu.
CUSTOM_DISABLE_HIDE,
// Custom context with handler that removes the plugin from the
// `navigator.plugins` list and consequently disables plugin load.
CUSTOM_NO_LIST,
};
class RequestContextHandler : public CefRequestContextHandler {
public:
explicit RequestContextHandler(PluginTestHandler* handler)
: handler_(handler) {}
bool OnBeforePluginLoad(const CefString& mime_type,
const CefString& plugin_url,
bool is_main_frame,
const CefString& top_origin_url,
CefRefPtr<CefWebPluginInfo> plugin_info,
PluginPolicy* plugin_policy) override {
const std::string& mime_type_str = mime_type;
EXPECT_STREQ("application/pdf", mime_type_str.c_str());
EXPECT_STREQ("chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/",
plugin_info->GetPath().ToString().c_str());
if (top_origin_url.empty()) {
if (!handler_->got_on_before_plugin_empty_origin_) {
// Checking for PDF support in the plugin frame (navigator.plugins
// listing, pdf load, etc).
handler_->got_on_before_plugin_empty_origin_.yes();
} else if (handler_->HasNoList()) {
// When listing is disabled there should be an additional check in the
// main frame for the navigator.plugins listing.
if (!handler_->got_on_before_plugin_empty_origin2_) {
EXPECT_TRUE(is_main_frame);
handler_->got_on_before_plugin_empty_origin2_.yes();
}
}
if (handler_->HasNoList()) {
// Remove the PDF plugin from the `navigator.plugins` list.
*plugin_policy = PLUGIN_POLICY_DISABLE;
return true;
}
// Ignore requests for building the plugin list.
return false;
}
// Should only get requests for the test origin.
EXPECT_STREQ(kPdfTestOrigin, top_origin_url.ToString().c_str());
if (!handler_->got_on_before_plugin_load_pdf_)
handler_->got_on_before_plugin_load_pdf_.yes();
else
NOTREACHED();
if (handler_->HasAllow()) {
*plugin_policy = PLUGIN_POLICY_ALLOW;
return true;
} else if (handler_->HasBlock()) {
*plugin_policy = PLUGIN_POLICY_BLOCK;
return true;
} else if (handler_->HasDisable()) {
*plugin_policy = PLUGIN_POLICY_DISABLE;
return true;
}
return false;
}
void Detach() { handler_ = nullptr; }
private:
PluginTestHandler* handler_;
IMPLEMENT_REFCOUNTING(RequestContextHandler);
};
PluginTestHandler(Mode mode, const std::string& url)
: mode_(mode), url_(url) {}
// Loading the PDF directly in the main frame instead of a sub-frame.
bool HasDirectPluginLoad() const { return url_ == kPdfDirectUrl; }
// Has a specified RequestContext but not necessarily a custom handler.
bool HasRequestContext() const { return mode_ != GLOBAL_DEFAULT; }
// Has a specified RequestContext and custom handler.
bool HasRequestContextHandler() const {
return mode_ != GLOBAL_DEFAULT && mode_ != GLOBAL_NO_HANDLER &&
mode_ != CUSTOM_NO_HANDLER;
}
// Using the global request context, either directly or with a custom handler.
bool HasGlobalRequestContext() const {
return mode_ >= GLOBAL_DEFAULT && mode_ <= GLOBAL_NO_LIST;
}
// Should allow the plugin load via the custom handler.
bool HasAllow() const {
return mode_ == GLOBAL_ALLOW || mode_ == CUSTOM_ALLOW;
}
// Should block the plugin load via the custom handler.
bool HasBlock() const {
return mode_ == GLOBAL_BLOCK_LOAD || mode_ == GLOBAL_BLOCK_HIDE ||
mode_ == CUSTOM_BLOCK_LOAD || mode_ == CUSTOM_BLOCK_HIDE;
}
// Should disable the plugin load via the custom handler.
bool HasDisable() const {
return mode_ == GLOBAL_DISABLE_HIDE || mode_ == CUSTOM_DISABLE_HIDE;
}
// Should exclude the plugin from the `navigator.plugins` list.
bool HasNoList() const {
return mode_ == GLOBAL_NO_LIST || mode_ == CUSTOM_NO_LIST;
}
// Should load the plugin via the context menu.
bool HasContextLoad() const {
return mode_ == GLOBAL_BLOCK_LOAD || mode_ == CUSTOM_BLOCK_LOAD;
}
// Should hide the plugin via the context menu.
bool HasContextHide() const {
return mode_ == GLOBAL_BLOCK_HIDE || mode_ == GLOBAL_DISABLE_HIDE ||
mode_ == CUSTOM_BLOCK_HIDE || mode_ == CUSTOM_DISABLE_HIDE;
}
std::string GetPluginNode() const {
return "document.getElementsByTagName('embed')[0]";
}
void WaitForNavigatorPlugins(CefRefPtr<CefFrame> frame) {
// We can no longer test for navigator.plugins because it returns empty
// as of https://www.chromestatus.com/feature/5741884322349056.
if (HasNoList()) {
// The plugin will not load. End the test.
EndTest();
} else if (HasBlock() || HasDisable()) {
// Wait for the plugin placeholder for the first PDF file to load. The
// test will continue from OnQuery.
WaitForPlaceholderLoad(frame);
} else {
// Wait for the first PDF file to load.
WaitForPluginLoad(frame);
}
}
void WaitForPlaceholderLoad(CefRefPtr<CefFrame> frame) {
// Keep track of the frame that contains the placeholder.
placeholder_frame_ = frame;
// Waits for the placeholder to load. This is indicated by the presence
// of the plugin node.
const std::string& code =
"(function waitForNode(i) {"
" setTimeout(function() {"
" var plugin = " +
GetPluginNode() +
";"
" if (plugin) {"
" window.testQuery({request:'placeholder_loaded'});"
" return;"
" }"
" if (--i) {"
" waitForNode(i);"
" } else {"
" console.log('Timeout waiting for plugin load');"
" }"
" }, 250)"
"})(4);";
frame->ExecuteJavaScript(code, frame->GetURL(), 0);
}
void WaitForPlaceholderHide() {
// Waits for the placeholder to be hidden (style set to display:none).
// See PluginPlaceholderBase::HidePlugin.
const std::string& code =
"var plugin = " + GetPluginNode() +
";"
"var observer = new MutationObserver(function(mutations) {"
" mutations.forEach(function(mutationRecord) {"
" window.testQuery({request:'placeholder_hidden'});"
" observer.disconnect();"
" });"
"});"
"observer.observe(plugin, "
" { attributes : true, attributeFilter : ['style'] });";
placeholder_frame_->ExecuteJavaScript(code, placeholder_frame_->GetURL(),
0);
placeholder_frame_ = nullptr;
}
void WaitForPluginLoad(CefRefPtr<CefFrame> frame) {
Add support for MimeHandlerViewInCrossProcessFrame (fixes issue #2727) The PDF loading documentation in extension_system.cc has be updated to describe the new code paths. To support delivery of input events to the mime handler renderer process it is now necessary to route events via the correct RWHV interface. For Aura-based platforms (Windows/Linux) this means RWHVAura::On*Event and for macOS this means RWHVMac::RouteOrProcess*Event. Since Aura uses UI event types these have become the source of truth on Aura-based platforms with conversion to Web event types when needed (primarily for OSR). This change also adds a timeout for CefProcessHostMsg_GetNewBrowserInfo to avoid a hung renderer process if the guest WebContents route is not registered via CefMimeHandlerViewGuestDelegate::OnGuestDetached as expected prior to CefBrowserInfoManager::OnGetNewBrowserInfo being called. This timeout can be disabled for testing purposes by passing the `--disable-new-browser-info-timeout` command-line flag. The `--disable-features=MimeHandlerViewInCrossProcessFrame` command-line flag can be used for a limited time to restore the previous implementation based on BrowserPlugin. That implementation will be deleted starting with the 3897 branch update. Known issues: - ExecuteJavaScript calls on the frame hosting the PDF extension will not be routed to the mime handler renderer process. - The PDF extension will not load successfully if blocked by ChromePluginPlaceholder and then manually continued via the "Run this plugin" context menu option (see https://crbug.com/533069#c41).
2020-01-23 22:58:01 +01:00
if (got_context_menu_dismissed_) {
// After context menu display. Destroy the test.
CefPostDelayedTask(TID_UI,
base::Bind(&PluginTestHandler::DestroyTest, this),
kPdfLoadDelayMs);
} else {
// Trigger the context menu.
CefPostDelayedTask(TID_UI,
base::Bind(&PluginTestHandler::TriggerContextMenu,
this, frame->GetBrowser()),
kPdfLoadDelayMs);
}
}
void EndTest() {
CefPostTask(TID_UI, base::Bind(&PluginTestHandler::DestroyTest, this));
}
CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() override {
return this;
}
void RunTest() override {
CefRefPtr<CefRequestContext> request_context;
if (HasRequestContext()) {
if (HasRequestContextHandler())
context_handler_ = new RequestContextHandler(this);
if (HasGlobalRequestContext()) {
// Use the global request context.
request_context = CefRequestContext::CreateContext(
CefRequestContext::GetGlobalContext(), context_handler_.get());
} else {
// Create the request context that will use an in-memory cache.
CefRequestContextSettings settings;
request_context =
CefRequestContext::CreateContext(settings, context_handler_.get());
}
}
// Create the browser.
CreateBrowser(url_, request_context);
// Time out the test after a reasonable period of time.
SetTestTimeout(5000 + kPdfLoadDelayMs);
}
CefRefPtr<CefResourceHandler> GetResourceHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) override {
const std::string& url = request->GetURL();
if (url == kPdfHtmlUrl) {
CefRefPtr<CefStreamReader> stream =
client::GetBinaryResourceReader("pdf.html");
return new CefStreamResourceHandler("text/html", stream);
} else if (url == kPdfDirectUrl) {
CefRefPtr<CefStreamReader> stream =
client::GetBinaryResourceReader("pdf.pdf");
return new CefStreamResourceHandler("application/pdf", stream);
}
return nullptr;
}
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) override {
bool is_pdf1 = false;
const std::string& url = frame->GetURL();
if (url == kPdfHtmlUrl) {
if (!got_on_load_end_html_)
got_on_load_end_html_.yes();
else
NOTREACHED();
} else if (url == kPdfDirectUrl) {
if (!got_on_load_end_pdf1_) {
got_on_load_end_pdf1_.yes();
is_pdf1 = true;
} else if (!got_on_load_end_pdf2_) {
got_on_load_end_pdf2_.yes();
} else {
NOTREACHED();
}
} else {
NOTREACHED();
}
if (HasNoList()) {
// If the plugin is not listed then the PDF documents will never load.
EXPECT_STREQ(kPdfHtmlUrl, url.c_str());
WaitForNavigatorPlugins(frame);
} else if (is_pdf1) {
// The first PDF document has loaded.
WaitForNavigatorPlugins(frame);
}
}
bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) override {
if (request == "placeholder_loaded") {
EXPECT_FALSE(got_placeholder_loaded_);
EXPECT_FALSE(got_placeholder_hidden_);
got_placeholder_loaded_.yes();
// The plugin placeholder has loaded. Trigger the context menu.
TriggerContextMenu(frame->GetBrowser());
} else if (request == "placeholder_hidden") {
EXPECT_TRUE(got_placeholder_loaded_);
EXPECT_FALSE(got_placeholder_hidden_);
got_placeholder_hidden_.yes();
if (HasContextLoad()) {
// Wait for the PDF plugin to load.
WaitForPluginLoad(frame);
} else {
// The plugin placeholder has been hidden. End the test.
EndTest();
}
} else {
NOTREACHED();
}
return true;
}
void TriggerContextMenu(CefRefPtr<CefBrowser> browser) {
CefMouseEvent mouse_event;
if (HasDirectPluginLoad()) {
// Somewhere in the main PDF viewing area (avoid left preview bar).
mouse_event.x = 400;
mouse_event.y = 200;
} else {
// Somewhere in the first plugin.
mouse_event.x = 100;
mouse_event.y = 100;
}
// Send right-click mouse down and mouse up to tigger context menu.
Add support for MimeHandlerViewInCrossProcessFrame (fixes issue #2727) The PDF loading documentation in extension_system.cc has be updated to describe the new code paths. To support delivery of input events to the mime handler renderer process it is now necessary to route events via the correct RWHV interface. For Aura-based platforms (Windows/Linux) this means RWHVAura::On*Event and for macOS this means RWHVMac::RouteOrProcess*Event. Since Aura uses UI event types these have become the source of truth on Aura-based platforms with conversion to Web event types when needed (primarily for OSR). This change also adds a timeout for CefProcessHostMsg_GetNewBrowserInfo to avoid a hung renderer process if the guest WebContents route is not registered via CefMimeHandlerViewGuestDelegate::OnGuestDetached as expected prior to CefBrowserInfoManager::OnGetNewBrowserInfo being called. This timeout can be disabled for testing purposes by passing the `--disable-new-browser-info-timeout` command-line flag. The `--disable-features=MimeHandlerViewInCrossProcessFrame` command-line flag can be used for a limited time to restore the previous implementation based on BrowserPlugin. That implementation will be deleted starting with the 3897 branch update. Known issues: - ExecuteJavaScript calls on the frame hosting the PDF extension will not be routed to the mime handler renderer process. - The PDF extension will not load successfully if blocked by ChromePluginPlaceholder and then manually continued via the "Run this plugin" context menu option (see https://crbug.com/533069#c41).
2020-01-23 22:58:01 +01:00
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_RIGHT, false, 1);
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_RIGHT, true, 1);
}
bool RunContextMenu(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefContextMenuParams> params,
CefRefPtr<CefMenuModel> model,
CefRefPtr<CefRunContextMenuCallback> callback) override {
EXPECT_FALSE(got_run_context_menu_);
got_run_context_menu_.yes();
if (HasContextLoad() || HasContextHide()) {
// Should have 4 elements -- plugin name, separator, run, hide.
EXPECT_EQ(4, model->GetCount());
// Wait for the placeholder to be hidden.
WaitForPlaceholderHide();
int command_id;
if (HasContextLoad()) {
// Execute the run command.
command_id = model->GetCommandIdAt(2);
} else {
// Execute the hide command.
command_id = model->GetCommandIdAt(3);
}
EXPECT_GE(command_id, MENU_ID_CUSTOM_FIRST);
EXPECT_LE(command_id, MENU_ID_CUSTOM_LAST);
callback->Continue(command_id, static_cast<EventFlags>(0));
} else {
// Do nothing with the context menu.
callback->Cancel();
}
return true;
}
void OnContextMenuDismissed(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame) override {
EXPECT_FALSE(got_context_menu_dismissed_);
got_context_menu_dismissed_.yes();
if (HasContextHide()) {
// Nothing to do here. The test will continue from OnQuery.
// TODO: Once block-then-load is working this should also be the case for
// HasContextLoad().
return;
}
EndTest();
}
void DestroyTest() override {
if (context_handler_.get()) {
context_handler_->Detach();
context_handler_ = nullptr;
}
if (HasContextLoad()) {
EXPECT_TRUE(got_placeholder_loaded_);
// TODO: Once block-then-load is working this value should be true.
EXPECT_FALSE(got_placeholder_hidden_);
} else if (HasContextHide()) {
EXPECT_TRUE(got_placeholder_loaded_);
EXPECT_TRUE(got_placeholder_hidden_);
} else {
EXPECT_FALSE(got_placeholder_loaded_);
EXPECT_FALSE(got_placeholder_hidden_);
}
EXPECT_FALSE(placeholder_frame_);
if (HasRequestContextHandler())
EXPECT_TRUE(got_on_before_plugin_empty_origin_);
else
EXPECT_FALSE(got_on_before_plugin_empty_origin_);
if (HasNoList()) {
EXPECT_TRUE(got_on_before_plugin_empty_origin2_);
EXPECT_FALSE(got_run_context_menu_);
EXPECT_FALSE(got_context_menu_dismissed_);
} else {
EXPECT_FALSE(got_on_before_plugin_empty_origin2_);
EXPECT_TRUE(got_run_context_menu_);
EXPECT_TRUE(got_context_menu_dismissed_);
}
if (url_ == kPdfHtmlUrl) {
// The HTML file will load the PDF twice in iframes.
EXPECT_TRUE(got_on_load_end_html_);
if (!HasNoList()) {
EXPECT_TRUE(got_on_load_end_pdf1_);
EXPECT_TRUE(got_on_load_end_pdf2_);
if (HasRequestContextHandler()) {
EXPECT_TRUE(got_on_before_plugin_load_pdf_);
}
}
} else if (url_ == kPdfDirectUrl) {
// Load the PDF file directly.
EXPECT_FALSE(got_on_load_end_html_);
EXPECT_TRUE(got_on_load_end_pdf1_);
EXPECT_FALSE(got_on_load_end_pdf2_);
if (HasRequestContextHandler()) {
EXPECT_TRUE(got_on_before_plugin_load_pdf_);
}
} else {
NOTREACHED();
}
if (!HasRequestContextHandler() || HasNoList()) {
EXPECT_FALSE(got_on_before_plugin_load_pdf_);
}
TestHandler::DestroyTest();
}
const Mode mode_;
const std::string url_;
CefRefPtr<CefFrame> placeholder_frame_;
TrackCallback got_on_before_plugin_empty_origin_;
TrackCallback got_on_before_plugin_empty_origin2_;
TrackCallback got_on_before_plugin_load_pdf_;
TrackCallback got_on_load_end_html_;
TrackCallback got_on_load_end_pdf1_;
TrackCallback got_on_load_end_pdf2_;
TrackCallback got_placeholder_loaded_;
TrackCallback got_placeholder_hidden_;
TrackCallback got_run_context_menu_;
TrackCallback got_context_menu_dismissed_;
CefRefPtr<RequestContextHandler> context_handler_;
IMPLEMENT_REFCOUNTING(PluginTestHandler);
};
} // namespace
#define RUN_TEST(name, type, url) \
TEST(PluginTest, name) { \
CefRefPtr<PluginTestHandler> handler = \
new PluginTestHandler(PluginTestHandler::type, url); \
handler->ExecuteTest(); \
ReleaseAndWaitForDestructor(handler); \
}
RUN_TEST(GlobalDefaultPdfDirect, GLOBAL_DEFAULT, kPdfDirectUrl)
RUN_TEST(GlobalDefaultPdfHtml, GLOBAL_DEFAULT, kPdfHtmlUrl)
RUN_TEST(GlobalNoHandlerPdfDirect, GLOBAL_NO_HANDLER, kPdfDirectUrl)
RUN_TEST(GlobalNoHandlerPdfHtml, GLOBAL_NO_HANDLER, kPdfHtmlUrl)
RUN_TEST(GlobalAllowPdfDirect, GLOBAL_ALLOW, kPdfDirectUrl)
RUN_TEST(GlobalAllowPdfHtml, GLOBAL_ALLOW, kPdfHtmlUrl)
RUN_TEST(GlobalBlockThenLoadPdfDirect, GLOBAL_BLOCK_LOAD, kPdfDirectUrl)
RUN_TEST(GlobalBlockThenLoadPdfHtml, GLOBAL_BLOCK_LOAD, kPdfHtmlUrl)
RUN_TEST(GlobalBlockThenHidePdfDirect, GLOBAL_BLOCK_HIDE, kPdfDirectUrl)
RUN_TEST(GlobalBlockThenHidePdfHtml, GLOBAL_BLOCK_HIDE, kPdfHtmlUrl)
RUN_TEST(GlobalDisableThenHidePdfDirect, GLOBAL_DISABLE_HIDE, kPdfDirectUrl)
RUN_TEST(GlobalDisableThenHidePdfHtml, GLOBAL_DISABLE_HIDE, kPdfHtmlUrl)
RUN_TEST(GlobalNoListHtml, GLOBAL_NO_LIST, kPdfHtmlUrl)
RUN_TEST(CustomNoHandlerPdfDirect, CUSTOM_NO_HANDLER, kPdfDirectUrl)
RUN_TEST(CustomNoHandlerPdfHtml, CUSTOM_NO_HANDLER, kPdfHtmlUrl)
RUN_TEST(CustomAllowPdfDirect, CUSTOM_ALLOW, kPdfDirectUrl)
RUN_TEST(CustomAllowPdfHtml, CUSTOM_ALLOW, kPdfHtmlUrl)
RUN_TEST(CustomBlockThenLoadPdfDirect, CUSTOM_BLOCK_LOAD, kPdfDirectUrl)
RUN_TEST(CustomBlockThenLoadPdfHtml, CUSTOM_BLOCK_LOAD, kPdfHtmlUrl)
RUN_TEST(CustomBlockThenHidePdfDirect, CUSTOM_BLOCK_HIDE, kPdfDirectUrl)
RUN_TEST(CustomBlockThenHidePdfHtml, CUSTOM_BLOCK_HIDE, kPdfHtmlUrl)
RUN_TEST(CustomDisableThenHidePdfDirect, CUSTOM_DISABLE_HIDE, kPdfDirectUrl)
RUN_TEST(CustomDisableThenHidePdfHtml, CUSTOM_DISABLE_HIDE, kPdfHtmlUrl)
RUN_TEST(CustomNoListHtml, CUSTOM_NO_LIST, kPdfHtmlUrl)
// Entry point for creating plugin browser test objects.
// Called from client_app_delegates.cc.
void CreatePluginBrowserTests(
client::ClientAppBrowser::DelegateSet& delegates) {
delegates.insert(new PluginBrowserTest);
}