diff options
Diffstat (limited to 'content/shell/browser')
62 files changed, 8106 insertions, 0 deletions
diff --git a/content/shell/browser/minimal_shell.cc b/content/shell/browser/minimal_shell.cc new file mode 100644 index 0000000..e7e88f8 --- /dev/null +++ b/content/shell/browser/minimal_shell.cc @@ -0,0 +1,54 @@ +// 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 "content/shell/browser/minimal_shell.h" + +#include "ui/aura/client/default_capture_client.h" +#include "ui/aura/focus_manager.h" +#include "ui/aura/root_window.h" +#include "ui/aura/test/test_activation_client.h" +#include "ui/views/corewm/compound_event_filter.h" +#include "ui/views/corewm/input_method_event_filter.h" + +namespace content { + +MinimalShell::MinimalShell(const gfx::Size& default_window_size) { + root_window_.reset(new aura::RootWindow( + aura::RootWindow::CreateParams( + gfx::Rect(default_window_size)))); + root_window_->Init(); + aura::client::SetStackingClient(root_window_.get(), this); + + focus_client_.reset(new aura::FocusManager); + aura::client::SetFocusClient(root_window_.get(), focus_client_.get()); + + root_window_event_filter_ = new views::corewm::CompoundEventFilter; + // Pass ownership of the filter to the root_window. + root_window_->SetEventFilter(root_window_event_filter_); + + input_method_filter_.reset(new views::corewm::InputMethodEventFilter( + root_window_->GetAcceleratedWidget())); + input_method_filter_->SetInputMethodPropertyInRootWindow( + root_window_.get()); + root_window_event_filter_->AddHandler(input_method_filter_.get()); + + test_activation_client_.reset( + new aura::test::TestActivationClient(root_window_.get())); + + capture_client_.reset( + new aura::client::DefaultCaptureClient(root_window_.get())); +} + +MinimalShell::~MinimalShell() { + root_window_event_filter_->RemoveHandler(input_method_filter_.get()); +} + +aura::Window* MinimalShell::GetDefaultParent( + aura::Window* context, + aura::Window* window, + const gfx::Rect& bounds) { + return root_window_.get(); +} + +} // namespace content diff --git a/content/shell/browser/minimal_shell.h b/content/shell/browser/minimal_shell.h new file mode 100644 index 0000000..1388da9 --- /dev/null +++ b/content/shell/browser/minimal_shell.h @@ -0,0 +1,67 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_MINIMAL_SHELL_H_ +#define CONTENT_SHELL_BROWSER_MINIMAL_SHELL_H_ + +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "ui/aura/client/stacking_client.h" + +namespace aura { +class RootWindow; +class Window; +namespace client { +class DefaultCaptureClient; +class FocusClient; +} +namespace test { +class TestActivationClient; +} +} + +namespace gfx { +class Rect; +class Size; +} + +namespace views { +namespace corewm { +class CompoundEventFilter; +class InputMethodEventFilter; +} +} + +namespace content { + +// Creates a minimal environment for running the shell. We can't pull in all of +// ash here, but we can create attach several of the same things we'd find in +// the ash parts of the code. +class MinimalShell : public aura::client::StackingClient { + public: + explicit MinimalShell(const gfx::Size& default_window_size); + virtual ~MinimalShell(); + + // Overridden from client::StackingClient: + virtual aura::Window* GetDefaultParent(aura::Window* context, + aura::Window* window, + const gfx::Rect& bounds) OVERRIDE; + + private: + scoped_ptr<aura::RootWindow> root_window_; + + // Owned by RootWindow + views::corewm::CompoundEventFilter* root_window_event_filter_; + + scoped_ptr<aura::client::DefaultCaptureClient> capture_client_; + scoped_ptr<views::corewm::InputMethodEventFilter> input_method_filter_; + scoped_ptr<aura::test::TestActivationClient> test_activation_client_; + scoped_ptr<aura::client::FocusClient> focus_client_; + + DISALLOW_COPY_AND_ASSIGN(MinimalShell); +}; + +} // namespace content; + +#endif // CONTENT_SHELL_BROWSER_MINIMAL_SHELL_H_ diff --git a/content/shell/browser/notify_done_forwarder.cc b/content/shell/browser/notify_done_forwarder.cc new file mode 100644 index 0000000..6d8f104 --- /dev/null +++ b/content/shell/browser/notify_done_forwarder.cc @@ -0,0 +1,33 @@ +// 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 "content/shell/browser/notify_done_forwarder.h" + +#include "content/shell/browser/webkit_test_controller.h" +#include "content/shell/common/shell_messages.h" + +namespace content { + +DEFINE_WEB_CONTENTS_USER_DATA_KEY(NotifyDoneForwarder); + +NotifyDoneForwarder::NotifyDoneForwarder(WebContents* web_contents) + : WebContentsObserver(web_contents) {} + +NotifyDoneForwarder::~NotifyDoneForwarder() {} + +bool NotifyDoneForwarder::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(NotifyDoneForwarder, message) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_TestFinishedInSecondaryWindow, + OnTestFinishedInSecondaryWindow) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void NotifyDoneForwarder::OnTestFinishedInSecondaryWindow() { + WebKitTestController::Get()->TestFinishedInSecondaryWindow(); +} + +} // namespace content diff --git a/content/shell/browser/notify_done_forwarder.h b/content/shell/browser/notify_done_forwarder.h new file mode 100644 index 0000000..e5a2f3f7 --- /dev/null +++ b/content/shell/browser/notify_done_forwarder.h @@ -0,0 +1,34 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_NOTIFY_DONE_FORWARDER_H_ +#define CONTENT_SHELL_BROWSER_NOTIFY_DONE_FORWARDER_H_ + +#include "base/basictypes.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" + +namespace content { + +class NotifyDoneForwarder : public WebContentsObserver, + public WebContentsUserData<NotifyDoneForwarder> { + public: + virtual ~NotifyDoneForwarder(); + + // WebContentsObserver implementation. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + private: + friend class WebContentsUserData<NotifyDoneForwarder>; + + explicit NotifyDoneForwarder(WebContents* web_contents); + + void OnTestFinishedInSecondaryWindow(); + + DISALLOW_COPY_AND_ASSIGN(NotifyDoneForwarder); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_NOTIFY_DONE_FORWARDER_H_ diff --git a/content/shell/browser/shell.cc b/content/shell/browser/shell.cc new file mode 100644 index 0000000..c8616d3 --- /dev/null +++ b/content/shell/browser/shell.cc @@ -0,0 +1,367 @@ +// 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 "content/shell/browser/shell.h" + +#include "base/auto_reset.h" +#include "base/command_line.h" +#include "base/message_loop/message_loop.h" +#include "base/path_service.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/devtools_manager.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_source.h" +#include "content/public/browser/notification_types.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_view.h" +#include "content/public/common/renderer_preferences.h" +#include "content/shell/browser/notify_done_forwarder.h" +#include "content/shell/browser/shell_browser_main_parts.h" +#include "content/shell/browser/shell_content_browser_client.h" +#include "content/shell/browser/shell_devtools_frontend.h" +#include "content/shell/browser/shell_javascript_dialog_manager.h" +#include "content/shell/browser/webkit_test_controller.h" +#include "content/shell/common/shell_messages.h" +#include "content/shell/common/shell_switches.h" + +namespace content { + +const int Shell::kDefaultTestWindowWidthDip = 800; +const int Shell::kDefaultTestWindowHeightDip = 600; + +std::vector<Shell*> Shell::windows_; +base::Callback<void(Shell*)> Shell::shell_created_callback_; + +bool Shell::quit_message_loop_ = true; + +class Shell::DevToolsWebContentsObserver : public WebContentsObserver { + public: + DevToolsWebContentsObserver(Shell* shell, WebContents* web_contents) + : WebContentsObserver(web_contents), + shell_(shell) { + } + + // WebContentsObserver + virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE { + shell_->OnDevToolsWebContentsDestroyed(); + } + + private: + Shell* shell_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsWebContentsObserver); +}; + +Shell::Shell(WebContents* web_contents) + : devtools_frontend_(NULL), + is_fullscreen_(false), + window_(NULL), + url_edit_view_(NULL), +#if defined(OS_WIN) && !defined(USE_AURA) + default_edit_wnd_proc_(0), +#endif + headless_(false) { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kDumpRenderTree)) + headless_ = true; + registrar_.Add(this, NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED, + Source<WebContents>(web_contents)); + windows_.push_back(this); + + if (!shell_created_callback_.is_null()) { + shell_created_callback_.Run(this); + shell_created_callback_.Reset(); + } +} + +Shell::~Shell() { + PlatformCleanUp(); + + for (size_t i = 0; i < windows_.size(); ++i) { + if (windows_[i] == this) { + windows_.erase(windows_.begin() + i); + break; + } + } + + if (windows_.empty() && quit_message_loop_) + base::MessageLoop::current()->PostTask(FROM_HERE, + base::MessageLoop::QuitClosure()); +} + +Shell* Shell::CreateShell(WebContents* web_contents, + const gfx::Size& initial_size) { + Shell* shell = new Shell(web_contents); + shell->PlatformCreateWindow(initial_size.width(), initial_size.height()); + + shell->web_contents_.reset(web_contents); + web_contents->SetDelegate(shell); + + shell->PlatformSetContents(); + + shell->PlatformResizeSubViews(); + + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) { + web_contents->GetMutableRendererPrefs()->use_custom_colors = false; + web_contents->GetRenderViewHost()->SyncRendererPrefs(); + } + + return shell; +} + +void Shell::CloseAllWindows() { + base::AutoReset<bool> auto_reset(&quit_message_loop_, false); + DevToolsManager::GetInstance()->CloseAllClientHosts(); + std::vector<Shell*> open_windows(windows_); + for (size_t i = 0; i < open_windows.size(); ++i) + open_windows[i]->Close(); + base::MessageLoop::current()->RunUntilIdle(); +} + +void Shell::SetShellCreatedCallback( + base::Callback<void(Shell*)> shell_created_callback) { + DCHECK(shell_created_callback_.is_null()); + shell_created_callback_ = shell_created_callback; +} + +Shell* Shell::FromRenderViewHost(RenderViewHost* rvh) { + for (size_t i = 0; i < windows_.size(); ++i) { + if (windows_[i]->web_contents() && + windows_[i]->web_contents()->GetRenderViewHost() == rvh) { + return windows_[i]; + } + } + return NULL; +} + +// static +void Shell::Initialize() { + PlatformInitialize( + gfx::Size(kDefaultTestWindowWidthDip, kDefaultTestWindowHeightDip)); +} + +Shell* Shell::CreateNewWindow(BrowserContext* browser_context, + const GURL& url, + SiteInstance* site_instance, + int routing_id, + const gfx::Size& initial_size) { + WebContents::CreateParams create_params(browser_context, site_instance); + create_params.routing_id = routing_id; + if (!initial_size.IsEmpty()) + create_params.initial_size = initial_size; + else + create_params.initial_size = + gfx::Size(kDefaultTestWindowWidthDip, kDefaultTestWindowHeightDip); + WebContents* web_contents = WebContents::Create(create_params); + Shell* shell = CreateShell(web_contents, create_params.initial_size); + if (!url.is_empty()) + shell->LoadURL(url); + return shell; +} + +void Shell::LoadURL(const GURL& url) { + LoadURLForFrame(url, std::string()); +} + +void Shell::LoadURLForFrame(const GURL& url, const std::string& frame_name) { + NavigationController::LoadURLParams params(url); + params.transition_type = PageTransitionFromInt( + PAGE_TRANSITION_TYPED | PAGE_TRANSITION_FROM_ADDRESS_BAR); + params.frame_name = frame_name; + web_contents_->GetController().LoadURLWithParams(params); + web_contents_->GetView()->Focus(); +} + +void Shell::GoBackOrForward(int offset) { + web_contents_->GetController().GoToOffset(offset); + web_contents_->GetView()->Focus(); +} + +void Shell::Reload() { + web_contents_->GetController().Reload(false); + web_contents_->GetView()->Focus(); +} + +void Shell::Stop() { + web_contents_->Stop(); + web_contents_->GetView()->Focus(); +} + +void Shell::UpdateNavigationControls() { + int current_index = web_contents_->GetController().GetCurrentEntryIndex(); + int max_index = web_contents_->GetController().GetEntryCount() - 1; + + PlatformEnableUIControl(BACK_BUTTON, current_index > 0); + PlatformEnableUIControl(FORWARD_BUTTON, current_index < max_index); + PlatformEnableUIControl(STOP_BUTTON, web_contents_->IsLoading()); +} + +void Shell::ShowDevTools() { + if (devtools_frontend_) { + devtools_frontend_->Focus(); + return; + } + devtools_frontend_ = ShellDevToolsFrontend::Show(web_contents()); + devtools_observer_.reset(new DevToolsWebContentsObserver( + this, devtools_frontend_->frontend_shell()->web_contents())); +} + +void Shell::CloseDevTools() { + if (!devtools_frontend_) + return; + devtools_observer_.reset(); + devtools_frontend_->Close(); + devtools_frontend_ = NULL; +} + +gfx::NativeView Shell::GetContentView() { + if (!web_contents_) + return NULL; + return web_contents_->GetView()->GetNativeView(); +} + +WebContents* Shell::OpenURLFromTab(WebContents* source, + const OpenURLParams& params) { + // The only one we implement for now. + DCHECK(params.disposition == CURRENT_TAB); + NavigationController::LoadURLParams load_url_params(params.url); + load_url_params.referrer = params.referrer; + load_url_params.transition_type = params.transition; + load_url_params.extra_headers = params.extra_headers; + load_url_params.should_replace_current_entry = + params.should_replace_current_entry; + + if (params.transferred_global_request_id != GlobalRequestID()) { + load_url_params.is_renderer_initiated = params.is_renderer_initiated; + load_url_params.transferred_global_request_id = + params.transferred_global_request_id; + } else if (params.is_renderer_initiated) { + load_url_params.is_renderer_initiated = true; + } + + source->GetController().LoadURLWithParams(load_url_params); + return source; +} + +void Shell::LoadingStateChanged(WebContents* source) { + UpdateNavigationControls(); + PlatformSetIsLoading(source->IsLoading()); +} + +void Shell::ToggleFullscreenModeForTab(WebContents* web_contents, + bool enter_fullscreen) { +#if defined(OS_ANDROID) + PlatformToggleFullscreenModeForTab(web_contents, enter_fullscreen); +#endif + if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) + return; + if (is_fullscreen_ != enter_fullscreen) { + is_fullscreen_ = enter_fullscreen; + web_contents->GetRenderViewHost()->WasResized(); + } +} + +bool Shell::IsFullscreenForTabOrPending(const WebContents* web_contents) const { +#if defined(OS_ANDROID) + return PlatformIsFullscreenForTabOrPending(web_contents); +#else + return is_fullscreen_; +#endif +} + +void Shell::RequestToLockMouse(WebContents* web_contents, + bool user_gesture, + bool last_unlocked_by_target) { + web_contents->GotResponseToLockMouseRequest(true); +} + +void Shell::CloseContents(WebContents* source) { + Close(); +} + +bool Shell::CanOverscrollContent() const { +#if defined(USE_AURA) + return true; +#else + return false; +#endif +} + +void Shell::WebContentsCreated(WebContents* source_contents, + int64 source_frame_id, + const string16& frame_name, + const GURL& target_url, + WebContents* new_contents) { + CreateShell(new_contents, source_contents->GetView()->GetContainerSize()); + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) + NotifyDoneForwarder::CreateForWebContents(new_contents); +} + +void Shell::DidNavigateMainFramePostCommit(WebContents* web_contents) { + PlatformSetAddressBarURL(web_contents->GetLastCommittedURL()); +} + +JavaScriptDialogManager* Shell::GetJavaScriptDialogManager() { + if (!dialog_manager_) + dialog_manager_.reset(new ShellJavaScriptDialogManager()); + return dialog_manager_.get(); +} + +bool Shell::AddMessageToConsole(WebContents* source, + int32 level, + const string16& message, + int32 line_no, + const string16& source_id) { + return CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree); +} + +void Shell::RendererUnresponsive(WebContents* source) { + if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) + return; + WebKitTestController::Get()->RendererUnresponsive(); +} + +void Shell::ActivateContents(WebContents* contents) { + contents->GetRenderViewHost()->Focus(); +} + +void Shell::DeactivateContents(WebContents* contents) { + contents->GetRenderViewHost()->Blur(); +} + +void Shell::WorkerCrashed(WebContents* source) { + if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) + return; + WebKitTestController::Get()->WorkerCrashed(); +} + +void Shell::Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED) { + std::pair<NavigationEntry*, bool>* title = + Details<std::pair<NavigationEntry*, bool> >(details).ptr(); + + if (title->first) { + string16 text = title->first->GetTitle(); + PlatformSetTitle(text); + } + } else { + NOTREACHED(); + } +} + +void Shell::OnDevToolsWebContentsDestroyed() { + devtools_observer_.reset(); + devtools_frontend_ = NULL; +} + +} // namespace content diff --git a/content/shell/browser/shell.h b/content/shell/browser/shell.h new file mode 100644 index 0000000..24c58c8 --- /dev/null +++ b/content/shell/browser/shell.h @@ -0,0 +1,286 @@ +// 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. +#ifndef CONTENT_SHELL_BROWSER_SHELL_H_ +#define CONTENT_SHELL_BROWSER_SHELL_H_ + + +#include <vector> + +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/web_contents_delegate.h" +#include "ipc/ipc_channel.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/size.h" + +#if defined(TOOLKIT_GTK) +#include <gtk/gtk.h> +#include "ui/base/gtk/gtk_signal.h" + +typedef struct _GtkToolItem GtkToolItem; +#elif defined(OS_ANDROID) +#include "base/android/scoped_java_ref.h" +#elif defined(USE_AURA) +#if defined(OS_CHROMEOS) +namespace content { +class MinimalShell; +} +#endif +namespace views { +class Widget; +class ViewsDelegate; +} +#endif + +class GURL; +namespace content { + +class BrowserContext; +class ShellDevToolsFrontend; +class ShellJavaScriptDialogManager; +class SiteInstance; +class WebContents; + +// This represents one window of the Content Shell, i.e. all the UI including +// buttons and url bar, as well as the web content area. +class Shell : public WebContentsDelegate, + public NotificationObserver { + public: + static const int kDefaultTestWindowWidthDip; + static const int kDefaultTestWindowHeightDip; + + virtual ~Shell(); + + void LoadURL(const GURL& url); + void LoadURLForFrame(const GURL& url, const std::string& frame_name); + void GoBackOrForward(int offset); + void Reload(); + void Stop(); + void UpdateNavigationControls(); + void Close(); + void ShowDevTools(); + void CloseDevTools(); +#if (defined(OS_WIN) && !defined(USE_AURA)) || \ + defined(TOOLKIT_GTK) || defined(OS_MACOSX) + // Resizes the main window to the given dimensions. + void SizeTo(int width, int height); +#endif + + // Do one time initialization at application startup. + static void Initialize(); + + static Shell* CreateNewWindow(BrowserContext* browser_context, + const GURL& url, + SiteInstance* site_instance, + int routing_id, + const gfx::Size& initial_size); + + // Returns the Shell object corresponding to the given RenderViewHost. + static Shell* FromRenderViewHost(RenderViewHost* rvh); + + // Returns the currently open windows. + static std::vector<Shell*>& windows() { return windows_; } + + // Closes all windows and returns. This runs a message loop. + static void CloseAllWindows(); + + // Closes all windows and exits. + static void PlatformExit(); + + // Used for content_browsertests. Called once. + static void SetShellCreatedCallback( + base::Callback<void(Shell*)> shell_created_callback); + + WebContents* web_contents() const { return web_contents_.get(); } + gfx::NativeWindow window() { return window_; } + +#if defined(OS_MACOSX) + // Public to be called by an ObjC bridge object. + void ActionPerformed(int control); + void URLEntered(std::string url_string); +#elif defined(OS_ANDROID) + // Registers the Android Java to native methods. + static bool Register(JNIEnv* env); +#endif + + // WebContentsDelegate + virtual WebContents* OpenURLFromTab(WebContents* source, + const OpenURLParams& params) OVERRIDE; + virtual void LoadingStateChanged(WebContents* source) OVERRIDE; +#if defined(OS_ANDROID) + virtual void LoadProgressChanged(WebContents* source, + double progress) OVERRIDE; +#endif + virtual void ToggleFullscreenModeForTab(WebContents* web_contents, + bool enter_fullscreen) OVERRIDE; + virtual bool IsFullscreenForTabOrPending( + const WebContents* web_contents) const OVERRIDE; + virtual void RequestToLockMouse(WebContents* web_contents, + bool user_gesture, + bool last_unlocked_by_target) OVERRIDE; + virtual void CloseContents(WebContents* source) OVERRIDE; + virtual bool CanOverscrollContent() const OVERRIDE; + virtual void WebContentsCreated(WebContents* source_contents, + int64 source_frame_id, + const string16& frame_name, + const GURL& target_url, + WebContents* new_contents) OVERRIDE; + virtual void DidNavigateMainFramePostCommit( + WebContents* web_contents) OVERRIDE; + virtual JavaScriptDialogManager* GetJavaScriptDialogManager() OVERRIDE; +#if defined(OS_MACOSX) + virtual void HandleKeyboardEvent( + WebContents* source, + const NativeWebKeyboardEvent& event) OVERRIDE; +#endif + virtual bool AddMessageToConsole(WebContents* source, + int32 level, + const string16& message, + int32 line_no, + const string16& source_id) OVERRIDE; + virtual void RendererUnresponsive(WebContents* source) OVERRIDE; + virtual void ActivateContents(WebContents* contents) OVERRIDE; + virtual void DeactivateContents(WebContents* contents) OVERRIDE; + virtual void WorkerCrashed(WebContents* source) OVERRIDE; + + private: + enum UIControl { + BACK_BUTTON, + FORWARD_BUTTON, + STOP_BUTTON + }; + + class DevToolsWebContentsObserver; + + explicit Shell(WebContents* web_contents); + + // Helper to create a new Shell given a newly created WebContents. + static Shell* CreateShell(WebContents* web_contents, + const gfx::Size& initial_size); + + // Helper for one time initialization of application + static void PlatformInitialize(const gfx::Size& default_window_size); + + // All the methods that begin with Platform need to be implemented by the + // platform specific Shell implementation. + // Called from the destructor to let each platform do any necessary cleanup. + void PlatformCleanUp(); + // Creates the main window GUI. + void PlatformCreateWindow(int width, int height); + // Links the WebContents into the newly created window. + void PlatformSetContents(); + // Resize the content area and GUI. + void PlatformResizeSubViews(); + // Enable/disable a button. + void PlatformEnableUIControl(UIControl control, bool is_enabled); + // Updates the url in the url bar. + void PlatformSetAddressBarURL(const GURL& url); + // Sets whether the spinner is spinning. + void PlatformSetIsLoading(bool loading); + // Set the title of shell window + void PlatformSetTitle(const string16& title); +#if defined(OS_ANDROID) + void PlatformToggleFullscreenModeForTab(WebContents* web_contents, + bool enter_fullscreen); + bool PlatformIsFullscreenForTabOrPending( + const WebContents* web_contents) const; +#endif + + gfx::NativeView GetContentView(); + + // NotificationObserver + virtual void Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) OVERRIDE; + + void OnDevToolsWebContentsDestroyed(); + +#if defined(OS_WIN) && !defined(USE_AURA) + static ATOM RegisterWindowClass(); + static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); + static LRESULT CALLBACK EditWndProc(HWND, UINT, WPARAM, LPARAM); +#elif defined(TOOLKIT_GTK) + CHROMEGTK_CALLBACK_0(Shell, void, OnBackButtonClicked); + CHROMEGTK_CALLBACK_0(Shell, void, OnForwardButtonClicked); + CHROMEGTK_CALLBACK_0(Shell, void, OnReloadButtonClicked); + CHROMEGTK_CALLBACK_0(Shell, void, OnStopButtonClicked); + CHROMEGTK_CALLBACK_0(Shell, void, OnURLEntryActivate); + CHROMEGTK_CALLBACK_0(Shell, gboolean, OnWindowDestroyed); + + CHROMEG_CALLBACK_3(Shell, gboolean, OnCloseWindowKeyPressed, GtkAccelGroup*, + GObject*, guint, GdkModifierType); + CHROMEG_CALLBACK_3(Shell, gboolean, OnNewWindowKeyPressed, GtkAccelGroup*, + GObject*, guint, GdkModifierType); + CHROMEG_CALLBACK_3(Shell, gboolean, OnHighlightURLView, GtkAccelGroup*, + GObject*, guint, GdkModifierType); + CHROMEG_CALLBACK_3(Shell, gboolean, OnReloadKeyPressed, GtkAccelGroup*, + GObject*, guint, GdkModifierType); +#endif + + scoped_ptr<ShellJavaScriptDialogManager> dialog_manager_; + + scoped_ptr<WebContents> web_contents_; + + scoped_ptr<DevToolsWebContentsObserver> devtools_observer_; + ShellDevToolsFrontend* devtools_frontend_; + + bool is_fullscreen_; + + gfx::NativeWindow window_; + gfx::NativeEditView url_edit_view_; + + // Notification manager + NotificationRegistrar registrar_; + +#if defined(OS_WIN) && !defined(USE_AURA) + WNDPROC default_edit_wnd_proc_; + static HINSTANCE instance_handle_; +#elif defined(TOOLKIT_GTK) + GtkWidget* vbox_; + + GtkToolItem* back_button_; + GtkToolItem* forward_button_; + GtkToolItem* reload_button_; + GtkToolItem* stop_button_; + + GtkWidget* spinner_; + GtkToolItem* spinner_item_; + + int content_width_; + int content_height_; + int ui_elements_height_; // height of menubar, toolbar, etc. +#elif defined(OS_ANDROID) + base::android::ScopedJavaGlobalRef<jobject> java_object_; +#elif defined(USE_AURA) +#if defined(OS_CHROMEOS) + static content::MinimalShell* minimal_shell_; +#endif + static views::ViewsDelegate* views_delegate_; + + views::Widget* window_widget_; +#elif defined(OS_MACOSX) + int content_width_; + int content_height_; +#endif + + bool headless_; + + // A container of all the open windows. We use a vector so we can keep track + // of ordering. + static std::vector<Shell*> windows_; + + static base::Callback<void(Shell*)> shell_created_callback_; + + // True if the destructur of Shell should post a quit closure on the current + // message loop if the destructed Shell object was the last one. + static bool quit_message_loop_; +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_H_ diff --git a/content/shell/browser/shell_android.cc b/content/shell/browser/shell_android.cc new file mode 100644 index 0000000..d827e96 --- /dev/null +++ b/content/shell/browser/shell_android.cc @@ -0,0 +1,93 @@ +// 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 "content/shell/browser/shell.h" + +#include <jni.h> + +#include "base/android/jni_string.h" +#include "base/android/scoped_java_ref.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/strings/string_piece.h" +#include "content/public/common/content_switches.h" +#include "content/shell/android/shell_manager.h" +#include "jni/Shell_jni.h" + +using base::android::AttachCurrentThread; +using base::android::ConvertUTF8ToJavaString; + +namespace content { + +void Shell::PlatformInitialize(const gfx::Size& default_window_size) { + CommandLine* command_line = CommandLine::ForCurrentProcess(); + DCHECK(command_line->HasSwitch(switches::kForceCompositingMode)); + DCHECK(command_line->HasSwitch(switches::kEnableThreadedCompositing)); +} + +void Shell::PlatformCleanUp() { +} + +void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) { +} + +void Shell::PlatformSetAddressBarURL(const GURL& url) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jstring> j_url = ConvertUTF8ToJavaString(env, url.spec()); + Java_Shell_onUpdateUrl(env, java_object_.obj(), j_url.obj()); +} + +void Shell::PlatformSetIsLoading(bool loading) { + JNIEnv* env = AttachCurrentThread(); + Java_Shell_setIsLoading(env, java_object_.obj(), loading); +} + +void Shell::PlatformCreateWindow(int width, int height) { + java_object_.Reset(AttachCurrentThread(), CreateShellView(this)); +} + +void Shell::PlatformSetContents() { + JNIEnv* env = AttachCurrentThread(); + Java_Shell_initFromNativeTabContents( + env, java_object_.obj(), reinterpret_cast<jint>(web_contents())); +} + +void Shell::PlatformResizeSubViews() { + // Not needed; subviews are bound. +} + +void Shell::PlatformSetTitle(const string16& title) { + NOTIMPLEMENTED(); +} + +void Shell::LoadProgressChanged(WebContents* source, double progress) { + JNIEnv* env = AttachCurrentThread(); + Java_Shell_onLoadProgressChanged(env, java_object_.obj(), progress); +} + +void Shell::PlatformToggleFullscreenModeForTab(WebContents* web_contents, + bool enter_fullscreen) { + JNIEnv* env = AttachCurrentThread(); + Java_Shell_toggleFullscreenModeForTab( + env, java_object_.obj(), enter_fullscreen); +} + +bool Shell::PlatformIsFullscreenForTabOrPending( + const WebContents* web_contents) const { + JNIEnv* env = AttachCurrentThread(); + return Java_Shell_isFullscreenForTabOrPending(env, java_object_.obj()); +} + +void Shell::Close() { + CloseShellView(java_object_.obj()); + java_object_.Reset(); + delete this; +} + +// static +bool Shell::Register(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace content diff --git a/content/shell/browser/shell_application_mac.h b/content/shell/browser/shell_application_mac.h new file mode 100644 index 0000000..bcba5a5 --- /dev/null +++ b/content/shell/browser/shell_application_mac.h @@ -0,0 +1,27 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_APPLICATION_MAC_H_ +#define CONTENT_SHELL_BROWSER_SHELL_APPLICATION_MAC_H_ + +#include "base/mac/scoped_sending_event.h" +#include "base/message_loop/message_pump_mac.h" + +@interface ShellCrApplication : NSApplication<CrAppProtocol, + CrAppControlProtocol> { + @private + BOOL handlingSendEvent_; +} + +// CrAppProtocol: +- (BOOL)isHandlingSendEvent; + +// CrAppControlProtocol: +- (void)setHandlingSendEvent:(BOOL)handlingSendEvent; + +- (IBAction)newDocument:(id)sender; + +@end + +#endif // CONTENT_SHELL_BROWSER_SHELL_APPLICATION_MAC_H_ diff --git a/content/shell/browser/shell_application_mac.mm b/content/shell/browser/shell_application_mac.mm new file mode 100644 index 0000000..e18df5b --- /dev/null +++ b/content/shell/browser/shell_application_mac.mm @@ -0,0 +1,39 @@ +// 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 "content/shell/browser/shell_application_mac.h" + +#include "base/auto_reset.h" +#include "content/public/common/url_constants.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/shell/browser/shell_content_browser_client.h" +#include "url/gurl.h" + +@implementation ShellCrApplication + +- (BOOL)isHandlingSendEvent { + return handlingSendEvent_; +} + +- (void)sendEvent:(NSEvent*)event { + base::AutoReset<BOOL> scoper(&handlingSendEvent_, YES); + [super sendEvent:event]; +} + +- (void)setHandlingSendEvent:(BOOL)handlingSendEvent { + handlingSendEvent_ = handlingSendEvent; +} + +- (IBAction)newDocument:(id)sender { + content::ShellBrowserContext* browserContext = + content::ShellContentBrowserClient::Get()->browser_context(); + content::Shell::CreateNewWindow(browserContext, + GURL(content::kAboutBlankURL), + NULL, + MSG_ROUTING_NONE, + gfx::Size()); +} + +@end diff --git a/content/shell/browser/shell_aura.cc b/content/shell/browser/shell_aura.cc new file mode 100644 index 0000000..03884da --- /dev/null +++ b/content/shell/browser/shell_aura.cc @@ -0,0 +1,374 @@ +// 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 "content/shell/browser/shell.h" + +#include "base/command_line.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "ui/aura/env.h" +#include "ui/aura/root_window.h" +#include "ui/aura/window.h" +#include "ui/base/accessibility/accessibility_types.h" +#include "ui/base/clipboard/clipboard.h" +#include "ui/base/events/event.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/screen.h" +#include "ui/views/controls/button/label_button.h" +#include "ui/views/controls/textfield/textfield.h" +#include "ui/views/controls/textfield/textfield_controller.h" +#include "ui/views/controls/webview/webview.h" +#include "ui/views/layout/fill_layout.h" +#include "ui/views/layout/grid_layout.h" +#include "ui/views/test/desktop_test_views_delegate.h" +#include "ui/views/view.h" +#include "ui/views/widget/desktop_aura/desktop_screen.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +#if defined(OS_CHROMEOS) +#include "chromeos/dbus/dbus_thread_manager.h" +#include "content/shell/browser/minimal_shell.h" +#include "ui/aura/test/test_screen.h" +#endif + +namespace content { + +namespace { +// ViewDelegate implementation for aura content shell +class ShellViewsDelegateAura : public views::DesktopTestViewsDelegate { + public: + ShellViewsDelegateAura() : use_transparent_windows_(false) { + } + + virtual ~ShellViewsDelegateAura() { + } + + void SetUseTransparentWindows(bool transparent) { + use_transparent_windows_ = transparent; + } + + // Overridden from views::TestViewsDelegate: + virtual bool UseTransparentWindows() const OVERRIDE { + return use_transparent_windows_; + } + + private: + bool use_transparent_windows_; + + DISALLOW_COPY_AND_ASSIGN(ShellViewsDelegateAura); +}; + +// Maintain the UI controls and web view for content shell +class ShellWindowDelegateView : public views::WidgetDelegateView, + public views::TextfieldController, + public views::ButtonListener { + public: + enum UIControl { + BACK_BUTTON, + FORWARD_BUTTON, + STOP_BUTTON + }; + + ShellWindowDelegateView(Shell* shell) + : shell_(shell), + toolbar_view_(new View), + contents_view_(new View) { + } + virtual ~ShellWindowDelegateView() {} + + // Update the state of UI controls + void SetAddressBarURL(const GURL& url) { + url_entry_->SetText(ASCIIToUTF16(url.spec())); + } + void SetWebContents(WebContents* web_contents) { + contents_view_->SetLayoutManager(new views::FillLayout()); + web_view_ = new views::WebView(web_contents->GetBrowserContext()); + web_view_->SetWebContents(web_contents); + web_contents->GetView()->Focus(); + contents_view_->AddChildView(web_view_); + Layout(); + } + void SetWindowTitle(const string16& title) { title_ = title; } + void EnableUIControl(UIControl control, bool is_enabled) { + if (control == BACK_BUTTON) { + back_button_->SetState(is_enabled ? views::CustomButton::STATE_NORMAL + : views::CustomButton::STATE_DISABLED); + } else if (control == FORWARD_BUTTON) { + forward_button_->SetState(is_enabled ? views::CustomButton::STATE_NORMAL + : views::CustomButton::STATE_DISABLED); + } else if (control == STOP_BUTTON) { + stop_button_->SetState(is_enabled ? views::CustomButton::STATE_NORMAL + : views::CustomButton::STATE_DISABLED); + } + } + + private: + // Initialize the UI control contained in shell window + void InitShellWindow() { + set_background(views::Background::CreateStandardPanelBackground()); + + views::GridLayout* layout = new views::GridLayout(this); + SetLayoutManager(layout); + + views::ColumnSet* column_set = layout->AddColumnSet(0); + column_set->AddPaddingColumn(0, 2); + column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, + views::GridLayout::USE_PREF, 0, 0); + column_set->AddPaddingColumn(0, 2); + + layout->AddPaddingRow(0, 2); + + // Add toolbar buttons and URL text field + { + layout->StartRow(0, 0); + views::GridLayout* toolbar_layout = new views::GridLayout(toolbar_view_); + toolbar_view_->SetLayoutManager(toolbar_layout); + + views::ColumnSet* toolbar_column_set = + toolbar_layout->AddColumnSet(0); + // Back button + back_button_ = new views::LabelButton(this, ASCIIToUTF16("Back")); + back_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); + gfx::Size back_button_size = back_button_->GetPreferredSize(); + toolbar_column_set->AddColumn(views::GridLayout::CENTER, + views::GridLayout::CENTER, 0, + views::GridLayout::FIXED, + back_button_size.width(), + back_button_size.width() / 2); + // Forward button + forward_button_ = new views::LabelButton(this, ASCIIToUTF16("Forward")); + forward_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); + gfx::Size forward_button_size = forward_button_->GetPreferredSize(); + toolbar_column_set->AddColumn(views::GridLayout::CENTER, + views::GridLayout::CENTER, 0, + views::GridLayout::FIXED, + forward_button_size.width(), + forward_button_size.width() / 2); + // Refresh button + refresh_button_ = new views::LabelButton(this, ASCIIToUTF16("Refresh")); + refresh_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); + gfx::Size refresh_button_size = refresh_button_->GetPreferredSize(); + toolbar_column_set->AddColumn(views::GridLayout::CENTER, + views::GridLayout::CENTER, 0, + views::GridLayout::FIXED, + refresh_button_size.width(), + refresh_button_size.width() / 2); + // Stop button + stop_button_ = new views::LabelButton(this, ASCIIToUTF16("Stop")); + stop_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON); + gfx::Size stop_button_size = stop_button_->GetPreferredSize(); + toolbar_column_set->AddColumn(views::GridLayout::CENTER, + views::GridLayout::CENTER, 0, + views::GridLayout::FIXED, + stop_button_size.width(), + stop_button_size.width() / 2); + toolbar_column_set->AddPaddingColumn(0, 2); + // URL entry + url_entry_ = new views::Textfield(); + url_entry_->SetController(this); + toolbar_column_set->AddColumn(views::GridLayout::FILL, + views::GridLayout::FILL, 1, + views::GridLayout::USE_PREF, 0, 0); + + // Fill up the first row + toolbar_layout->StartRow(0, 0); + toolbar_layout->AddView(back_button_); + toolbar_layout->AddView(forward_button_); + toolbar_layout->AddView(refresh_button_); + toolbar_layout->AddView(stop_button_); + toolbar_layout->AddView(url_entry_); + + layout->AddView(toolbar_view_); + } + + layout->AddPaddingRow(0, 5); + + // Add web contents view as the second row + { + layout->StartRow(1, 0); + layout->AddView(contents_view_); + } + + layout->AddPaddingRow(0, 5); + } + // Overridden from TextfieldController + virtual void ContentsChanged(views::Textfield* sender, + const string16& new_contents) OVERRIDE { + } + virtual bool HandleKeyEvent(views::Textfield* sender, + const ui::KeyEvent& key_event) OVERRIDE { + if (sender == url_entry_ && key_event.key_code() == ui::VKEY_RETURN) { + std::string text = UTF16ToUTF8(url_entry_->text()); + GURL url(text); + if (!url.has_scheme()) { + url = GURL(std::string("http://") + std::string(text)); + url_entry_->SetText(ASCIIToUTF16(url.spec())); + } + shell_->LoadURL(url); + return true; + } + return false; + } + + // Overridden from ButtonListener + virtual void ButtonPressed(views::Button* sender, + const ui::Event& event) OVERRIDE { + if (sender == back_button_) + shell_->GoBackOrForward(-1); + else if (sender == forward_button_) + shell_->GoBackOrForward(1); + else if (sender == refresh_button_) + shell_->Reload(); + else if (sender == stop_button_) + shell_->Stop(); + } + + // Overridden from WidgetDelegateView + virtual bool CanResize() const OVERRIDE { return true; } + virtual bool CanMaximize() const OVERRIDE { return true; } + virtual string16 GetWindowTitle() const OVERRIDE { + return title_; + } + virtual void WindowClosing() OVERRIDE { + if (shell_) { + delete shell_; + shell_ = NULL; + } + } + virtual View* GetContentsView() OVERRIDE { return this; } + + // Overridden from View + virtual void ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) OVERRIDE { + if (details.is_add && details.child == this) { + InitShellWindow(); + } + } + + private: + // Hold a reference of Shell for deleting it when the window is closing + Shell* shell_; + + // Window title + string16 title_; + + // Toolbar view contains forward/backward/reload button and URL entry + View* toolbar_view_; + views::LabelButton* back_button_; + views::LabelButton* forward_button_; + views::LabelButton* refresh_button_; + views::LabelButton* stop_button_; + views::Textfield* url_entry_; + + // Contents view contains the web contents view + View* contents_view_; + views::WebView* web_view_; + + DISALLOW_COPY_AND_ASSIGN(ShellWindowDelegateView); +}; + +} // namespace + +#if defined(OS_CHROMEOS) +MinimalShell* Shell::minimal_shell_ = NULL; +#endif +views::ViewsDelegate* Shell::views_delegate_ = NULL; + +// static +void Shell::PlatformInitialize(const gfx::Size& default_window_size) { +#if defined(OS_CHROMEOS) + chromeos::DBusThreadManager::Initialize(); + gfx::Screen::SetScreenInstance( + gfx::SCREEN_TYPE_NATIVE, aura::TestScreen::Create()); + minimal_shell_ = new content::MinimalShell(default_window_size); +#else + gfx::Screen::SetScreenInstance( + gfx::SCREEN_TYPE_NATIVE, views::CreateDesktopScreen()); +#endif + views_delegate_ = new ShellViewsDelegateAura(); +} + +void Shell::PlatformExit() { +#if defined(OS_CHROMEOS) + if (minimal_shell_) + delete minimal_shell_; +#endif + if (views_delegate_) + delete views_delegate_; +#if defined(OS_CHROMEOS) + chromeos::DBusThreadManager::Shutdown(); +#endif + aura::Env::DeleteInstance(); +} + +void Shell::PlatformCleanUp() { +} + +void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) { + ShellWindowDelegateView* delegate_view = + static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate()); + if (control == BACK_BUTTON) { + delegate_view->EnableUIControl(ShellWindowDelegateView::BACK_BUTTON, + is_enabled); + } else if (control == FORWARD_BUTTON) { + delegate_view->EnableUIControl(ShellWindowDelegateView::FORWARD_BUTTON, + is_enabled); + } else if (control == STOP_BUTTON) { + delegate_view->EnableUIControl(ShellWindowDelegateView::STOP_BUTTON, + is_enabled); + } +} + +void Shell::PlatformSetAddressBarURL(const GURL& url) { + ShellWindowDelegateView* delegate_view = + static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate()); + delegate_view->SetAddressBarURL(url); +} + +void Shell::PlatformSetIsLoading(bool loading) { +} + +void Shell::PlatformCreateWindow(int width, int height) { +#if defined(OS_CHROMEOS) + window_widget_ = + views::Widget::CreateWindowWithContextAndBounds( + new ShellWindowDelegateView(this), + minimal_shell_->GetDefaultParent(NULL, NULL, gfx::Rect()), + gfx::Rect(0, 0, width, height)); +#else + window_widget_ = + views::Widget::CreateWindowWithBounds(new ShellWindowDelegateView(this), + gfx::Rect(0, 0, width, height)); +#endif + + window_ = window_widget_->GetNativeWindow(); + // Call ShowRootWindow on RootWindow created by MinimalShell without + // which XWindow owned by RootWindow doesn't get mapped. + window_->GetRootWindow()->ShowRootWindow(); + window_widget_->Show(); +} + +void Shell::PlatformSetContents() { + ShellWindowDelegateView* delegate_view = + static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate()); + delegate_view->SetWebContents(web_contents_.get()); +} + +void Shell::PlatformResizeSubViews() { +} + +void Shell::Close() { + window_widget_->Close(); +} + +void Shell::PlatformSetTitle(const string16& title) { + ShellWindowDelegateView* delegate_view = + static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate()); + delegate_view->SetWindowTitle(title); + window_widget_->UpdateWindowTitle(); +} + +} // namespace content diff --git a/content/shell/browser/shell_browser_context.cc b/content/shell/browser/shell_browser_context.cc new file mode 100644 index 0000000..c6dbb99 --- /dev/null +++ b/content/shell/browser/shell_browser_context.cc @@ -0,0 +1,215 @@ +// 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 "content/shell/browser/shell_browser_context.h" + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/environment.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/threading/thread.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/resource_context.h" +#include "content/public/browser/storage_partition.h" +#include "content/public/common/content_switches.h" +#include "content/shell/browser/shell_download_manager_delegate.h" +#include "content/shell/browser/shell_url_request_context_getter.h" +#include "content/shell/common/shell_switches.h" + +#if defined(OS_WIN) +#include "base/base_paths_win.h" +#elif defined(OS_LINUX) +#include "base/nix/xdg_util.h" +#elif defined(OS_MACOSX) +#include "base/base_paths_mac.h" +#endif + +namespace content { + +class ShellBrowserContext::ShellResourceContext : public ResourceContext { + public: + ShellResourceContext() : getter_(NULL) {} + virtual ~ShellResourceContext() {} + + // ResourceContext implementation: + virtual net::HostResolver* GetHostResolver() OVERRIDE { + CHECK(getter_); + return getter_->host_resolver(); + } + virtual net::URLRequestContext* GetRequestContext() OVERRIDE { + CHECK(getter_); + return getter_->GetURLRequestContext(); + } + virtual bool AllowMicAccess(const GURL& origin) OVERRIDE { + return false; + } + virtual bool AllowCameraAccess(const GURL& origin) OVERRIDE { + return false; + } + + void set_url_request_context_getter(ShellURLRequestContextGetter* getter) { + getter_ = getter; + } + + private: + ShellURLRequestContextGetter* getter_; + + DISALLOW_COPY_AND_ASSIGN(ShellResourceContext); +}; + +ShellBrowserContext::ShellBrowserContext(bool off_the_record, + net::NetLog* net_log) + : off_the_record_(off_the_record), + net_log_(net_log), + ignore_certificate_errors_(false), + resource_context_(new ShellResourceContext) { + InitWhileIOAllowed(); +} + +ShellBrowserContext::~ShellBrowserContext() { + if (resource_context_) { + BrowserThread::DeleteSoon( + BrowserThread::IO, FROM_HERE, resource_context_.release()); + } +} + +void ShellBrowserContext::InitWhileIOAllowed() { + CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + if (cmd_line->HasSwitch(switches::kIgnoreCertificateErrors) || + cmd_line->HasSwitch(switches::kDumpRenderTree)) { + ignore_certificate_errors_ = true; + } + if (cmd_line->HasSwitch(switches::kContentShellDataPath)) { + path_ = cmd_line->GetSwitchValuePath(switches::kContentShellDataPath); + return; + } +#if defined(OS_WIN) + CHECK(PathService::Get(base::DIR_LOCAL_APP_DATA, &path_)); + path_ = path_.Append(std::wstring(L"content_shell")); +#elif defined(OS_LINUX) + scoped_ptr<base::Environment> env(base::Environment::Create()); + base::FilePath config_dir( + base::nix::GetXDGDirectory(env.get(), + base::nix::kXdgConfigHomeEnvVar, + base::nix::kDotConfigDir)); + path_ = config_dir.Append("content_shell"); +#elif defined(OS_MACOSX) + CHECK(PathService::Get(base::DIR_APP_DATA, &path_)); + path_ = path_.Append("Chromium Content Shell"); +#elif defined(OS_ANDROID) + CHECK(PathService::Get(base::DIR_ANDROID_APP_DATA, &path_)); + path_ = path_.Append(FILE_PATH_LITERAL("content_shell")); +#else + NOTIMPLEMENTED(); +#endif + + if (!base::PathExists(path_)) + file_util::CreateDirectory(path_); +} + +base::FilePath ShellBrowserContext::GetPath() const { + return path_; +} + +bool ShellBrowserContext::IsOffTheRecord() const { + return off_the_record_; +} + +DownloadManagerDelegate* ShellBrowserContext::GetDownloadManagerDelegate() { + DownloadManager* manager = BrowserContext::GetDownloadManager(this); + + if (!download_manager_delegate_.get()) { + download_manager_delegate_ = new ShellDownloadManagerDelegate(); + download_manager_delegate_->SetDownloadManager(manager); + CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + if (cmd_line->HasSwitch(switches::kDumpRenderTree)) { + download_manager_delegate_->SetDownloadBehaviorForTesting( + path_.Append(FILE_PATH_LITERAL("downloads"))); + } + } + + return download_manager_delegate_.get(); +} + +net::URLRequestContextGetter* ShellBrowserContext::GetRequestContext() { + return GetDefaultStoragePartition(this)->GetURLRequestContext(); +} + +net::URLRequestContextGetter* ShellBrowserContext::CreateRequestContext( + ProtocolHandlerMap* protocol_handlers) { + DCHECK(!url_request_getter_.get()); + url_request_getter_ = new ShellURLRequestContextGetter( + ignore_certificate_errors_, + GetPath(), + BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::IO), + BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::FILE), + protocol_handlers, + net_log_); + resource_context_->set_url_request_context_getter(url_request_getter_.get()); + return url_request_getter_.get(); +} + +net::URLRequestContextGetter* + ShellBrowserContext::GetRequestContextForRenderProcess( + int renderer_child_id) { + return GetRequestContext(); +} + +net::URLRequestContextGetter* + ShellBrowserContext::GetMediaRequestContext() { + return GetRequestContext(); +} + +net::URLRequestContextGetter* + ShellBrowserContext::GetMediaRequestContextForRenderProcess( + int renderer_child_id) { + return GetRequestContext(); +} + +net::URLRequestContextGetter* + ShellBrowserContext::GetMediaRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory) { + return GetRequestContext(); +} + +void ShellBrowserContext::RequestMIDISysExPermission( + int render_process_id, + int render_view_id, + const GURL& requesting_frame, + const MIDISysExPermissionCallback& callback) { + // Always reject requests for LayoutTests for now. + // TODO(toyoshim): Make it programmable to improve test coverage. + if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) { + callback.Run(false); + return; + } + // TODO(toyoshim): Implement. http://crbug.com/257618 . + callback.Run(false); +} + +net::URLRequestContextGetter* + ShellBrowserContext::CreateRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory, + ProtocolHandlerMap* protocol_handlers) { + return NULL; +} + +ResourceContext* ShellBrowserContext::GetResourceContext() { + return resource_context_.get(); +} + +GeolocationPermissionContext* + ShellBrowserContext::GetGeolocationPermissionContext() { + return NULL; +} + +quota::SpecialStoragePolicy* ShellBrowserContext::GetSpecialStoragePolicy() { + return NULL; +} + +} // namespace content diff --git a/content/shell/browser/shell_browser_context.h b/content/shell/browser/shell_browser_context.h new file mode 100644 index 0000000..ec71591 --- /dev/null +++ b/content/shell/browser/shell_browser_context.h @@ -0,0 +1,83 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_BROWSER_CONTEXT_H_ +#define CONTENT_SHELL_BROWSER_SHELL_BROWSER_CONTEXT_H_ + +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/content_browser_client.h" +#include "net/url_request/url_request_job_factory.h" + +namespace net { +class NetLog; +} + +namespace content { + +class DownloadManagerDelegate; +class ResourceContext; +class ShellDownloadManagerDelegate; +class ShellURLRequestContextGetter; + +class ShellBrowserContext : public BrowserContext { + public: + ShellBrowserContext(bool off_the_record, net::NetLog* net_log); + virtual ~ShellBrowserContext(); + + // BrowserContext implementation. + virtual base::FilePath GetPath() const OVERRIDE; + virtual bool IsOffTheRecord() const OVERRIDE; + virtual DownloadManagerDelegate* GetDownloadManagerDelegate() OVERRIDE; + virtual net::URLRequestContextGetter* GetRequestContext() OVERRIDE; + virtual net::URLRequestContextGetter* GetRequestContextForRenderProcess( + int renderer_child_id) OVERRIDE; + virtual net::URLRequestContextGetter* GetMediaRequestContext() OVERRIDE; + virtual net::URLRequestContextGetter* GetMediaRequestContextForRenderProcess( + int renderer_child_id) OVERRIDE; + virtual net::URLRequestContextGetter* + GetMediaRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory) OVERRIDE; + virtual void RequestMIDISysExPermission( + int render_process_id, + int render_view_id, + const GURL& requesting_frame, + const MIDISysExPermissionCallback& callback) OVERRIDE; + virtual ResourceContext* GetResourceContext() OVERRIDE; + virtual GeolocationPermissionContext* + GetGeolocationPermissionContext() OVERRIDE; + virtual quota::SpecialStoragePolicy* GetSpecialStoragePolicy() OVERRIDE; + + net::URLRequestContextGetter* CreateRequestContext( + ProtocolHandlerMap* protocol_handlers); + net::URLRequestContextGetter* CreateRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory, + ProtocolHandlerMap* protocol_handlers); + + private: + class ShellResourceContext; + + // Performs initialization of the ShellBrowserContext while IO is still + // allowed on the current thread. + void InitWhileIOAllowed(); + + bool off_the_record_; + net::NetLog* net_log_; + bool ignore_certificate_errors_; + base::FilePath path_; + scoped_ptr<ShellResourceContext> resource_context_; + scoped_refptr<ShellDownloadManagerDelegate> download_manager_delegate_; + scoped_refptr<ShellURLRequestContextGetter> url_request_getter_; + + DISALLOW_COPY_AND_ASSIGN(ShellBrowserContext); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_BROWSER_CONTEXT_H_ diff --git a/content/shell/browser/shell_browser_main.cc b/content/shell/browser/shell_browser_main.cc new file mode 100644 index 0000000..ac520d6 --- /dev/null +++ b/content/shell/browser/shell_browser_main.cc @@ -0,0 +1,220 @@ +// 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 "content/shell/browser/shell_browser_main.h" + +#include <iostream> + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/files/scoped_temp_dir.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/sys_string_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_restrictions.h" +#include "content/public/browser/browser_main_runner.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/webkit_test_controller.h" +#include "content/shell/common/shell_switches.h" +#include "content/shell/common/webkit_test_helpers.h" +#include "net/base/net_util.h" +#include "webkit/support/webkit_support.h" + +#if defined(OS_ANDROID) +#include "base/run_loop.h" +#include "content/shell/browser/shell_layout_tests_android.h" +#endif + +namespace { + +GURL GetURLForLayoutTest(const std::string& test_name, + base::FilePath* current_working_directory, + bool* enable_pixel_dumping, + std::string* expected_pixel_hash) { + // A test name is formated like file:///path/to/test'--pixel-test'pixelhash + std::string path_or_url = test_name; + std::string pixel_switch; + std::string pixel_hash; + std::string::size_type separator_position = path_or_url.find('\''); + if (separator_position != std::string::npos) { + pixel_switch = path_or_url.substr(separator_position + 1); + path_or_url.erase(separator_position); + } + separator_position = pixel_switch.find('\''); + if (separator_position != std::string::npos) { + pixel_hash = pixel_switch.substr(separator_position + 1); + pixel_switch.erase(separator_position); + } + if (enable_pixel_dumping) { + *enable_pixel_dumping = + (pixel_switch == "--pixel-test" || pixel_switch == "-p"); + } + if (expected_pixel_hash) + *expected_pixel_hash = pixel_hash; + + GURL test_url; +#if defined(OS_ANDROID) + if (content::GetTestUrlForAndroid(path_or_url, &test_url)) + return test_url; +#endif + + test_url = GURL(path_or_url); + if (!(test_url.is_valid() && test_url.has_scheme())) { + // We're outside of the message loop here, and this is a test. + base::ThreadRestrictions::ScopedAllowIO allow_io; +#if defined(OS_WIN) + std::wstring wide_path_or_url = + base::SysNativeMBToWide(path_or_url); + base::FilePath local_file(wide_path_or_url); +#else + base::FilePath local_file(path_or_url); +#endif + if (!base::PathExists(local_file)) { + local_file = content::GetWebKitRootDirFilePath() + .Append(FILE_PATH_LITERAL("LayoutTests")).Append(local_file); + } + test_url = net::FilePathToFileURL(base::MakeAbsoluteFilePath(local_file)); + } + base::FilePath local_path; + if (current_working_directory) { + // We're outside of the message loop here, and this is a test. + base::ThreadRestrictions::ScopedAllowIO allow_io; + if (net::FileURLToFilePath(test_url, &local_path)) + *current_working_directory = local_path.DirName(); + else + file_util::GetCurrentDirectory(current_working_directory); + } + return test_url; +} + +bool GetNextTest(const CommandLine::StringVector& args, + size_t* position, + std::string* test) { + if (*position >= args.size()) + return false; + if (args[*position] == FILE_PATH_LITERAL("-")) + return !!std::getline(std::cin, *test, '\n'); +#if defined(OS_WIN) + *test = WideToUTF8(args[(*position)++]); +#else + *test = args[(*position)++]; +#endif + return true; +} + +} // namespace + +// Main routine for running as the Browser process. +int ShellBrowserMain( + const content::MainFunctionParams& parameters, + const scoped_ptr<content::BrowserMainRunner>& main_runner) { + bool layout_test_mode = + CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree); + base::ScopedTempDir browser_context_path_for_layout_tests; + + if (layout_test_mode) { + CHECK(browser_context_path_for_layout_tests.CreateUniqueTempDir()); + CHECK(!browser_context_path_for_layout_tests.path().MaybeAsASCII().empty()); + CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kContentShellDataPath, + browser_context_path_for_layout_tests.path().MaybeAsASCII()); + +#if defined(OS_ANDROID) + content::EnsureInitializeForAndroidLayoutTests(); +#endif + } + + int exit_code = main_runner->Initialize(parameters); + DCHECK_LT(exit_code, 0) + << "BrowserMainRunner::Initialize failed in ShellBrowserMain"; + + if (exit_code >= 0) + return exit_code; + + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kCheckLayoutTestSysDeps)) { + base::MessageLoop::current()->PostTask(FROM_HERE, + base::MessageLoop::QuitClosure()); + main_runner->Run(); + content::Shell::CloseAllWindows(); + main_runner->Shutdown(); + return 0; + } + + if (layout_test_mode) { + content::WebKitTestController test_controller; + { + // We're outside of the message loop here, and this is a test. + base::ThreadRestrictions::ScopedAllowIO allow_io; + base::FilePath temp_path; + file_util::GetTempDir(&temp_path); + test_controller.SetTempPath(temp_path); + } + std::string test_string; + CommandLine::StringVector args = + CommandLine::ForCurrentProcess()->GetArgs(); + size_t command_line_position = 0; + bool ran_at_least_once = false; + +#if defined(OS_ANDROID) + std::cout << "#READY\n"; + std::cout.flush(); +#endif + + while (GetNextTest(args, &command_line_position, &test_string)) { + if (test_string.empty()) + continue; + if (test_string == "QUIT") + break; + + bool enable_pixel_dumps; + std::string pixel_hash; + base::FilePath cwd; + GURL test_url = GetURLForLayoutTest( + test_string, &cwd, &enable_pixel_dumps, &pixel_hash); + if (!content::WebKitTestController::Get()->PrepareForLayoutTest( + test_url, cwd, enable_pixel_dumps, pixel_hash)) { + break; + } + + ran_at_least_once = true; +#if defined(OS_ANDROID) + // The message loop on Android is provided by the system, and does not + // offer a blocking Run() method. For layout tests, use a nested loop + // together with a base::RunLoop so it can block until a QuitClosure. + base::RunLoop run_loop; + run_loop.Run(); +#else + main_runner->Run(); +#endif + + if (!content::WebKitTestController::Get()->ResetAfterLayoutTest()) + break; + } + if (!ran_at_least_once) { + base::MessageLoop::current()->PostTask(FROM_HERE, + base::MessageLoop::QuitClosure()); + main_runner->Run(); + } + +#if defined(OS_ANDROID) + // Android should only execute Shutdown() here when running layout tests. + main_runner->Shutdown(); +#endif + + exit_code = 0; + } + +#if !defined(OS_ANDROID) + if (!layout_test_mode) + exit_code = main_runner->Run(); + + main_runner->Shutdown(); +#endif + + return exit_code; +} diff --git a/content/shell/browser/shell_browser_main.h b/content/shell/browser/shell_browser_main.h new file mode 100644 index 0000000..d361fa3 --- /dev/null +++ b/content/shell/browser/shell_browser_main.h @@ -0,0 +1,19 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_BROWSER_MAIN_H_ +#define CONTENT_SHELL_BROWSER_SHELL_BROWSER_MAIN_H_ + +#include "base/memory/scoped_ptr.h" + +namespace content { +class BrowserMainRunner; +struct MainFunctionParams; +} + +int ShellBrowserMain( + const content::MainFunctionParams& parameters, + const scoped_ptr<content::BrowserMainRunner>& main_runner); + +#endif // CONTENT_SHELL_BROWSER_SHELL_BROWSER_MAIN_H_ diff --git a/content/shell/browser/shell_browser_main_parts.cc b/content/shell/browser/shell_browser_main_parts.cc new file mode 100644 index 0000000..6f7da00 --- /dev/null +++ b/content/shell/browser/shell_browser_main_parts.cc @@ -0,0 +1,175 @@ +// 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 "content/shell/browser/shell_browser_main_parts.h" + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/message_loop/message_loop.h" +#include "base/threading/thread.h" +#include "base/threading/thread_restrictions.h" +#include "cc/base/switches.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/storage_partition.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/main_function_params.h" +#include "content/public/common/url_constants.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/shell/browser/shell_devtools_delegate.h" +#include "content/shell/browser/shell_net_log.h" +#include "content/shell/common/shell_switches.h" +#include "grit/net_resources.h" +#include "net/base/net_module.h" +#include "net/base/net_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "url/gurl.h" +#include "webkit/browser/quota/quota_manager.h" + +#if defined(ENABLE_PLUGINS) +#include "content/public/browser/plugin_service.h" +#include "content/shell/browser/shell_plugin_service_filter.h" +#endif + +#if defined(OS_ANDROID) +#include "net/android/network_change_notifier_factory_android.h" +#include "net/base/network_change_notifier.h" +#endif + +#if defined(USE_AURA) && defined(USE_X11) +#include "ui/base/touch/touch_factory_x11.h" +#endif + +namespace content { + +namespace { + +// Default quota for each origin is 5MB. +const int kDefaultLayoutTestQuotaBytes = 5 * 1024 * 1024; + +GURL GetStartupURL() { + CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kContentBrowserTest)) + return GURL(); + const CommandLine::StringVector& args = command_line->GetArgs(); + +#if defined(OS_ANDROID) + // Delay renderer creation on Android until surface is ready. + return GURL(); +#endif + + if (args.empty()) + return GURL("http://www.google.com/"); + + GURL url(args[0]); + if (url.is_valid() && url.has_scheme()) + return url; + + return net::FilePathToFileURL(base::FilePath(args[0])); +} + +base::StringPiece PlatformResourceProvider(int key) { + if (key == IDR_DIR_HEADER_HTML) { + base::StringPiece html_data = + ui::ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_DIR_HEADER_HTML); + return html_data; + } + return base::StringPiece(); +} + +} // namespace + +ShellBrowserMainParts::ShellBrowserMainParts( + const MainFunctionParams& parameters) + : BrowserMainParts(), parameters_(parameters), run_message_loop_(true) {} + +ShellBrowserMainParts::~ShellBrowserMainParts() { +} + +#if !defined(OS_MACOSX) +void ShellBrowserMainParts::PreMainMessageLoopStart() { +#if defined(USE_AURA) && defined(USE_X11) + ui::TouchFactory::SetTouchDeviceListFromCommandLine(); +#endif +} +#endif + +void ShellBrowserMainParts::PostMainMessageLoopStart() { +#if defined(OS_ANDROID) + base::MessageLoopForUI::current()->Start(); +#endif +} + +void ShellBrowserMainParts::PreEarlyInitialization() { +#if defined(OS_ANDROID) + net::NetworkChangeNotifier::SetFactory( + new net::NetworkChangeNotifierFactoryAndroid()); + + CommandLine::ForCurrentProcess()->AppendSwitch( + cc::switches::kCompositeToMailbox); +#endif +} + +void ShellBrowserMainParts::PreMainMessageLoopRun() { + net_log_.reset(new ShellNetLog()); + browser_context_.reset(new ShellBrowserContext(false, net_log_.get())); + off_the_record_browser_context_.reset( + new ShellBrowserContext(true, net_log_.get())); + + Shell::Initialize(); + net::NetModule::SetResourceProvider(PlatformResourceProvider); + + devtools_delegate_.reset(new ShellDevToolsDelegate(browser_context_.get())); + + if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) { + Shell::CreateNewWindow(browser_context_.get(), + GetStartupURL(), + NULL, + MSG_ROUTING_NONE, + gfx::Size()); + } + + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) { + quota::QuotaManager* quota_manager = + BrowserContext::GetDefaultStoragePartition(browser_context()) + ->GetQuotaManager(); + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind("a::QuotaManager::SetTemporaryGlobalOverrideQuota, + quota_manager, + kDefaultLayoutTestQuotaBytes * + quota::QuotaManager::kPerHostTemporaryPortion, + quota::QuotaCallback())); +#if defined(ENABLE_PLUGINS) + PluginService* plugin_service = PluginService::GetInstance(); + plugin_service_filter_.reset(new ShellPluginServiceFilter); + plugin_service->SetFilter(plugin_service_filter_.get()); +#endif + } + + if (parameters_.ui_task) { + parameters_.ui_task->Run(); + delete parameters_.ui_task; + run_message_loop_ = false; + } +} + +bool ShellBrowserMainParts::MainMessageLoopRun(int* result_code) { + return !run_message_loop_; +} + +void ShellBrowserMainParts::PostMainMessageLoopRun() { +#if defined(USE_AURA) + Shell::PlatformExit(); +#endif + if (devtools_delegate_) + devtools_delegate_->Stop(); + browser_context_.reset(); + off_the_record_browser_context_.reset(); +} + +} // namespace diff --git a/content/shell/browser/shell_browser_main_parts.h b/content/shell/browser/shell_browser_main_parts.h new file mode 100644 index 0000000..990e1c5 --- /dev/null +++ b/content/shell/browser/shell_browser_main_parts.h @@ -0,0 +1,70 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_BROWSER_MAIN_PARTS_H_ +#define CONTENT_SHELL_BROWSER_SHELL_BROWSER_MAIN_PARTS_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/browser_main_parts.h" +#include "content/public/common/main_function_params.h" + +namespace base { +class Thread; +} + +namespace net { +class NetLog; +} + +namespace content { + +class ShellBrowserContext; +class ShellDevToolsDelegate; +class ShellPluginServiceFilter; + +class ShellBrowserMainParts : public BrowserMainParts { + public: + explicit ShellBrowserMainParts(const MainFunctionParams& parameters); + virtual ~ShellBrowserMainParts(); + + // BrowserMainParts overrides. + virtual void PreEarlyInitialization() OVERRIDE; + virtual void PreMainMessageLoopStart() OVERRIDE; + virtual void PostMainMessageLoopStart() OVERRIDE; + virtual void PreMainMessageLoopRun() OVERRIDE; + virtual bool MainMessageLoopRun(int* result_code) OVERRIDE; + virtual void PostMainMessageLoopRun() OVERRIDE; + + ShellDevToolsDelegate* devtools_delegate() { + return devtools_delegate_.get(); + } + + ShellBrowserContext* browser_context() { return browser_context_.get(); } + ShellBrowserContext* off_the_record_browser_context() { + return off_the_record_browser_context_.get(); + } + + net::NetLog* net_log() { return net_log_.get(); } + + private: + scoped_ptr<net::NetLog> net_log_; + scoped_ptr<ShellBrowserContext> browser_context_; + scoped_ptr<ShellBrowserContext> off_the_record_browser_context_; + + // For running content_browsertests. + const MainFunctionParams parameters_; + bool run_message_loop_; + + scoped_ptr<ShellDevToolsDelegate> devtools_delegate_; +#if defined(ENABLE_PLUGINS) + scoped_ptr<ShellPluginServiceFilter> plugin_service_filter_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ShellBrowserMainParts); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_BROWSER_MAIN_PARTS_H_ diff --git a/content/shell/browser/shell_browser_main_parts_mac.mm b/content/shell/browser/shell_browser_main_parts_mac.mm new file mode 100644 index 0000000..a933705 --- /dev/null +++ b/content/shell/browser/shell_browser_main_parts_mac.mm @@ -0,0 +1,25 @@ +// 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 "content/shell/browser/shell_browser_main_parts.h" + +#import <Cocoa/Cocoa.h> + +#include "base/mac/bundle_locations.h" +#include "base/mac/scoped_nsobject.h" +#include "content/shell/browser/shell_application_mac.h" + +namespace content { + +void ShellBrowserMainParts::PreMainMessageLoopStart() { + // Force the NSApplication subclass to be used. + [ShellCrApplication sharedApplication]; + + base::scoped_nsobject<NSNib> nib( + [[NSNib alloc] initWithNibNamed:@"MainMenu" + bundle:base::mac::FrameworkBundle()]); + [nib instantiateNibWithOwner:NSApp topLevelObjects:nil]; +} + +} // namespace content diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc new file mode 100644 index 0000000..715eafd --- /dev/null +++ b/content/shell/browser/shell_content_browser_client.cc @@ -0,0 +1,275 @@ +// 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 "content/shell/browser/shell_content_browser_client.h" + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_types.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/resource_dispatcher_host.h" +#include "content/public/browser/storage_partition.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/url_constants.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/shell/browser/shell_browser_main_parts.h" +#include "content/shell/browser/shell_devtools_delegate.h" +#include "content/shell/browser/shell_message_filter.h" +#include "content/shell/browser/shell_net_log.h" +#include "content/shell/browser/shell_quota_permission_context.h" +#include "content/shell/browser/shell_resource_dispatcher_host_delegate.h" +#include "content/shell/browser/shell_web_contents_view_delegate_creator.h" +#include "content/shell/browser/webkit_test_controller.h" +#include "content/shell/common/shell_messages.h" +#include "content/shell/common/shell_switches.h" +#include "content/shell/common/webkit_test_helpers.h" +#include "content/shell/geolocation/shell_access_token_store.h" +#include "net/url_request/url_request_context_getter.h" +#include "url/gurl.h" +#include "webkit/common/webpreferences.h" + +#if defined(OS_ANDROID) +#include "base/android/path_utils.h" +#include "base/path_service.h" +#include "base/platform_file.h" +#include "content/shell/android/shell_descriptors.h" +#endif + +namespace content { + +namespace { + +ShellContentBrowserClient* g_browser_client; +bool g_swap_processes_for_redirect = false; + +} // namespace + +ShellContentBrowserClient* ShellContentBrowserClient::Get() { + return g_browser_client; +} + +void ShellContentBrowserClient::SetSwapProcessesForRedirect(bool swap) { + g_swap_processes_for_redirect = swap; +} + +ShellContentBrowserClient::ShellContentBrowserClient() + : shell_browser_main_parts_(NULL) { + DCHECK(!g_browser_client); + g_browser_client = this; + if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) + return; + webkit_source_dir_ = GetWebKitRootDirFilePath(); +} + +ShellContentBrowserClient::~ShellContentBrowserClient() { + g_browser_client = NULL; +} + +BrowserMainParts* ShellContentBrowserClient::CreateBrowserMainParts( + const MainFunctionParams& parameters) { + shell_browser_main_parts_ = new ShellBrowserMainParts(parameters); + return shell_browser_main_parts_; +} + +void ShellContentBrowserClient::RenderProcessHostCreated( + RenderProcessHost* host) { + if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) + return; + host->GetChannel()->AddFilter(new ShellMessageFilter( + host->GetID(), + BrowserContext::GetDefaultStoragePartition(browser_context()) + ->GetDatabaseTracker(), + BrowserContext::GetDefaultStoragePartition(browser_context()) + ->GetQuotaManager(), + BrowserContext::GetDefaultStoragePartition(browser_context()) + ->GetURLRequestContext())); + host->Send(new ShellViewMsg_SetWebKitSourceDir(webkit_source_dir_)); + registrar_.Add(this, + NOTIFICATION_RENDERER_PROCESS_CREATED, + Source<RenderProcessHost>(host)); + registrar_.Add(this, + NOTIFICATION_RENDERER_PROCESS_TERMINATED, + Source<RenderProcessHost>(host)); +} + +net::URLRequestContextGetter* ShellContentBrowserClient::CreateRequestContext( + BrowserContext* content_browser_context, + ProtocolHandlerMap* protocol_handlers) { + ShellBrowserContext* shell_browser_context = + ShellBrowserContextForBrowserContext(content_browser_context); + return shell_browser_context->CreateRequestContext(protocol_handlers); +} + +net::URLRequestContextGetter* +ShellContentBrowserClient::CreateRequestContextForStoragePartition( + BrowserContext* content_browser_context, + const base::FilePath& partition_path, + bool in_memory, + ProtocolHandlerMap* protocol_handlers) { + ShellBrowserContext* shell_browser_context = + ShellBrowserContextForBrowserContext(content_browser_context); + return shell_browser_context->CreateRequestContextForStoragePartition( + partition_path, in_memory, protocol_handlers); +} + +bool ShellContentBrowserClient::IsHandledURL(const GURL& url) { + if (!url.is_valid()) + return false; + DCHECK_EQ(url.scheme(), StringToLowerASCII(url.scheme())); + // Keep in sync with ProtocolHandlers added by + // ShellURLRequestContextGetter::GetURLRequestContext(). + static const char* const kProtocolList[] = { + chrome::kBlobScheme, + chrome::kFileSystemScheme, + chrome::kChromeUIScheme, + chrome::kChromeDevToolsScheme, + chrome::kDataScheme, + chrome::kFileScheme, + }; + for (size_t i = 0; i < arraysize(kProtocolList); ++i) { + if (url.scheme() == kProtocolList[i]) + return true; + } + return false; +} + +void ShellContentBrowserClient::AppendExtraCommandLineSwitches( + CommandLine* command_line, int child_process_id) { + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) + command_line->AppendSwitch(switches::kDumpRenderTree); + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kExposeInternalsForTesting)) + command_line->AppendSwitch(switches::kExposeInternalsForTesting); +} + +void ShellContentBrowserClient::OverrideWebkitPrefs( + RenderViewHost* render_view_host, + const GURL& url, + WebPreferences* prefs) { + if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) + return; + WebKitTestController::Get()->OverrideWebkitPrefs(prefs); +} + +void ShellContentBrowserClient::ResourceDispatcherHostCreated() { + resource_dispatcher_host_delegate_.reset( + new ShellResourceDispatcherHostDelegate()); + ResourceDispatcherHost::Get()->SetDelegate( + resource_dispatcher_host_delegate_.get()); +} + +std::string ShellContentBrowserClient::GetDefaultDownloadName() { + return "download"; +} + +bool ShellContentBrowserClient::SupportsBrowserPlugin( + content::BrowserContext* browser_context, const GURL& url) { + return CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableBrowserPluginForAllViewTypes); +} + +WebContentsViewDelegate* ShellContentBrowserClient::GetWebContentsViewDelegate( + WebContents* web_contents) { +#if !defined(USE_AURA) + return CreateShellWebContentsViewDelegate(web_contents); +#else + return NULL; +#endif +} + +QuotaPermissionContext* +ShellContentBrowserClient::CreateQuotaPermissionContext() { + return new ShellQuotaPermissionContext(); +} + +net::NetLog* ShellContentBrowserClient::GetNetLog() { + return shell_browser_main_parts_->net_log(); +} + +bool ShellContentBrowserClient::ShouldSwapProcessesForRedirect( + ResourceContext* resource_context, + const GURL& current_url, + const GURL& new_url) { + return g_swap_processes_for_redirect; +} + +#if defined(OS_ANDROID) +void ShellContentBrowserClient::GetAdditionalMappedFilesForChildProcess( + const CommandLine& command_line, + int child_process_id, + std::vector<content::FileDescriptorInfo>* mappings) { + int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ; + base::FilePath pak_file; + bool r = PathService::Get(base::DIR_ANDROID_APP_DATA, &pak_file); + CHECK(r); + pak_file = pak_file.Append(FILE_PATH_LITERAL("paks")); + pak_file = pak_file.Append(FILE_PATH_LITERAL("content_shell.pak")); + + base::PlatformFile f = + base::CreatePlatformFile(pak_file, flags, NULL, NULL); + if (f == base::kInvalidPlatformFileValue) { + NOTREACHED() << "Failed to open file when creating renderer process: " + << "content_shell.pak"; + } + mappings->push_back( + content::FileDescriptorInfo(kShellPakDescriptor, + base::FileDescriptor(f, true))); +} +#endif + +void ShellContentBrowserClient::Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type) { + case NOTIFICATION_RENDERER_PROCESS_CREATED: { + registrar_.Remove(this, + NOTIFICATION_RENDERER_PROCESS_CREATED, + source); + registrar_.Remove(this, + NOTIFICATION_RENDERER_PROCESS_TERMINATED, + source); + break; + } + + case NOTIFICATION_RENDERER_PROCESS_TERMINATED: { + registrar_.Remove(this, + NOTIFICATION_RENDERER_PROCESS_CREATED, + source); + registrar_.Remove(this, + NOTIFICATION_RENDERER_PROCESS_TERMINATED, + source); + break; + } + + default: + NOTREACHED(); + } +} + +ShellBrowserContext* ShellContentBrowserClient::browser_context() { + return shell_browser_main_parts_->browser_context(); +} + +ShellBrowserContext* + ShellContentBrowserClient::off_the_record_browser_context() { + return shell_browser_main_parts_->off_the_record_browser_context(); +} + +AccessTokenStore* ShellContentBrowserClient::CreateAccessTokenStore() { + return new ShellAccessTokenStore(browser_context()); +} + +ShellBrowserContext* +ShellContentBrowserClient::ShellBrowserContextForBrowserContext( + BrowserContext* content_browser_context) { + if (content_browser_context == browser_context()) + return browser_context(); + DCHECK_EQ(content_browser_context, off_the_record_browser_context()); + return off_the_record_browser_context(); +} + +} // namespace content diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h new file mode 100644 index 0000000..15285ca --- /dev/null +++ b/content/shell/browser/shell_content_browser_client.h @@ -0,0 +1,103 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_CONTENT_BROWSER_CLIENT_H_ +#define CONTENT_SHELL_BROWSER_SHELL_CONTENT_BROWSER_CLIENT_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/platform_file.h" +#include "content/public/browser/content_browser_client.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" + +namespace content { + +class ShellBrowserContext; +class ShellBrowserMainParts; +class ShellResourceDispatcherHostDelegate; + +class ShellContentBrowserClient : public ContentBrowserClient, + public NotificationObserver { + public: + // Gets the current instance. + static ShellContentBrowserClient* Get(); + + static void SetSwapProcessesForRedirect(bool swap); + + ShellContentBrowserClient(); + virtual ~ShellContentBrowserClient(); + + // ContentBrowserClient overrides. + virtual BrowserMainParts* CreateBrowserMainParts( + const MainFunctionParams& parameters) OVERRIDE; + virtual void RenderProcessHostCreated(RenderProcessHost* host) OVERRIDE; + virtual net::URLRequestContextGetter* CreateRequestContext( + BrowserContext* browser_context, + ProtocolHandlerMap* protocol_handlers) OVERRIDE; + virtual net::URLRequestContextGetter* CreateRequestContextForStoragePartition( + BrowserContext* browser_context, + const base::FilePath& partition_path, + bool in_memory, + ProtocolHandlerMap* protocol_handlers) OVERRIDE; + virtual bool IsHandledURL(const GURL& url) OVERRIDE; + virtual void AppendExtraCommandLineSwitches(CommandLine* command_line, + int child_process_id) OVERRIDE; + virtual void OverrideWebkitPrefs(RenderViewHost* render_view_host, + const GURL& url, + WebPreferences* prefs) OVERRIDE; + virtual void ResourceDispatcherHostCreated() OVERRIDE; + virtual AccessTokenStore* CreateAccessTokenStore() OVERRIDE; + virtual std::string GetDefaultDownloadName() OVERRIDE; + virtual bool SupportsBrowserPlugin(content::BrowserContext* browser_context, + const GURL& url) OVERRIDE; + virtual WebContentsViewDelegate* GetWebContentsViewDelegate( + WebContents* web_contents) OVERRIDE; + virtual QuotaPermissionContext* CreateQuotaPermissionContext() OVERRIDE; + virtual net::NetLog* GetNetLog() OVERRIDE; + virtual bool ShouldSwapProcessesForRedirect(ResourceContext* resource_context, + const GURL& current_url, + const GURL& new_url) OVERRIDE; + +#if defined(OS_ANDROID) + virtual void GetAdditionalMappedFilesForChildProcess( + const CommandLine& command_line, + int child_process_id, + std::vector<content::FileDescriptorInfo>* mappings) OVERRIDE; +#endif + + // NotificationObserver implementation. + virtual void Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) OVERRIDE; + + ShellBrowserContext* browser_context(); + ShellBrowserContext* off_the_record_browser_context(); + ShellResourceDispatcherHostDelegate* resource_dispatcher_host_delegate() { + return resource_dispatcher_host_delegate_.get(); + } + ShellBrowserMainParts* shell_browser_main_parts() { + return shell_browser_main_parts_; + } + + private: + ShellBrowserContext* ShellBrowserContextForBrowserContext( + BrowserContext* content_browser_context); + + scoped_ptr<ShellResourceDispatcherHostDelegate> + resource_dispatcher_host_delegate_; + + base::FilePath webkit_source_dir_; + + ShellBrowserMainParts* shell_browser_main_parts_; + + NotificationRegistrar registrar_; +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_CONTENT_BROWSER_CLIENT_H_ diff --git a/content/shell/browser/shell_devtools_delegate.cc b/content/shell/browser/shell_devtools_delegate.cc new file mode 100644 index 0000000..0a01405 --- /dev/null +++ b/content/shell/browser/shell_devtools_delegate.cc @@ -0,0 +1,119 @@ +// 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 "content/shell/browser/shell_devtools_delegate.h" + +#include <vector> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/strings/string_number_conversions.h" +#include "content/public/browser/devtools_http_handler.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/url_constants.h" +#include "content/shell/browser/shell.h" +#include "grit/shell_resources.h" +#include "net/socket/tcp_listen_socket.h" +#include "ui/base/resource/resource_bundle.h" + +#if defined(OS_ANDROID) +#include "content/public/browser/android/devtools_auth.h" +#include "net/socket/unix_domain_socket_posix.h" +#endif + +namespace { + +net::StreamListenSocketFactory* CreateSocketFactory() { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); +#if defined(OS_ANDROID) + std::string socket_name = "content_shell_devtools_remote"; + if (command_line.HasSwitch(switches::kRemoteDebuggingSocketName)) { + socket_name = command_line.GetSwitchValueASCII( + switches::kRemoteDebuggingSocketName); + } + return new net::UnixDomainSocketWithAbstractNamespaceFactory( + socket_name, "", base::Bind(&content::CanUserConnectToDevTools)); +#else + // See if the user specified a port on the command line (useful for + // automation). If not, use an ephemeral port by specifying 0. + int port = 0; + if (command_line.HasSwitch(switches::kRemoteDebuggingPort)) { + int temp_port; + std::string port_str = + command_line.GetSwitchValueASCII(switches::kRemoteDebuggingPort); + if (base::StringToInt(port_str, &temp_port) && + temp_port > 0 && temp_port < 65535) { + port = temp_port; + } else { + DLOG(WARNING) << "Invalid http debugger port number " << temp_port; + } + } + return new net::TCPListenSocketFactory("127.0.0.1", port); +#endif +} +} // namespace + +namespace content { + +ShellDevToolsDelegate::ShellDevToolsDelegate(BrowserContext* browser_context) + : browser_context_(browser_context) { + // Note that Content Shell always used bundled DevTools frontend, + // even on Android, because the shell is used for running layout tests. + devtools_http_handler_ = + DevToolsHttpHandler::Start(CreateSocketFactory(), std::string(), this); +} + +ShellDevToolsDelegate::~ShellDevToolsDelegate() { +} + +void ShellDevToolsDelegate::Stop() { + // The call below destroys this. + devtools_http_handler_->Stop(); +} + +std::string ShellDevToolsDelegate::GetDiscoveryPageHTML() { + return ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_CONTENT_SHELL_DEVTOOLS_DISCOVERY_PAGE).as_string(); +} + +bool ShellDevToolsDelegate::BundlesFrontendResources() { + return true; +} + +base::FilePath ShellDevToolsDelegate::GetDebugFrontendDir() { + return base::FilePath(); +} + +std::string ShellDevToolsDelegate::GetPageThumbnailData(const GURL& url) { + return std::string(); +} + +RenderViewHost* ShellDevToolsDelegate::CreateNewTarget() { + Shell* shell = Shell::CreateNewWindow(browser_context_, + GURL(kAboutBlankURL), + NULL, + MSG_ROUTING_NONE, + gfx::Size()); + return shell->web_contents()->GetRenderViewHost(); +} + +DevToolsHttpHandlerDelegate::TargetType +ShellDevToolsDelegate::GetTargetType(RenderViewHost*) { + return kTargetTypeTab; +} + +std::string ShellDevToolsDelegate::GetViewDescription( + content::RenderViewHost*) { + return std::string(); +} + +scoped_refptr<net::StreamListenSocket> +ShellDevToolsDelegate::CreateSocketForTethering( + net::StreamListenSocket::Delegate* delegate, + std::string* name) { + return NULL; +} + +} // namespace content diff --git a/content/shell/browser/shell_devtools_delegate.h b/content/shell/browser/shell_devtools_delegate.h new file mode 100644 index 0000000..a218738 --- /dev/null +++ b/content/shell/browser/shell_devtools_delegate.h @@ -0,0 +1,50 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_DEVTOOLS_DELEGATE_H_ +#define CONTENT_SHELL_BROWSER_SHELL_DEVTOOLS_DELEGATE_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "content/public/browser/devtools_http_handler_delegate.h" + +namespace content { + +class BrowserContext; +class DevToolsHttpHandler; + +class ShellDevToolsDelegate : public DevToolsHttpHandlerDelegate { + public: + explicit ShellDevToolsDelegate(BrowserContext* browser_context); + virtual ~ShellDevToolsDelegate(); + + // Stops http server. + void Stop(); + + // DevToolsHttpProtocolHandler::Delegate overrides. + virtual std::string GetDiscoveryPageHTML() OVERRIDE; + virtual bool BundlesFrontendResources() OVERRIDE; + virtual base::FilePath GetDebugFrontendDir() OVERRIDE; + virtual std::string GetPageThumbnailData(const GURL& url) OVERRIDE; + virtual RenderViewHost* CreateNewTarget() OVERRIDE; + virtual TargetType GetTargetType(RenderViewHost*) OVERRIDE; + virtual std::string GetViewDescription(content::RenderViewHost*) OVERRIDE; + virtual scoped_refptr<net::StreamListenSocket> CreateSocketForTethering( + net::StreamListenSocket::Delegate* delegate, + std::string* name) OVERRIDE; + + DevToolsHttpHandler* devtools_http_handler() { + return devtools_http_handler_; + } + + private: + BrowserContext* browser_context_; + DevToolsHttpHandler* devtools_http_handler_; + + DISALLOW_COPY_AND_ASSIGN(ShellDevToolsDelegate); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_DEVTOOLS_DELEGATE_H_ diff --git a/content/shell/browser/shell_devtools_frontend.cc b/content/shell/browser/shell_devtools_frontend.cc new file mode 100644 index 0000000..8c3f74b --- /dev/null +++ b/content/shell/browser/shell_devtools_frontend.cc @@ -0,0 +1,107 @@ +// 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 "content/shell/browser/shell_devtools_frontend.h" + +#include "base/command_line.h" +#include "base/path_service.h" +#include "content/public/browser/devtools_http_handler.h" +#include "content/public/browser/devtools_manager.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "content/public/common/content_client.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/shell/browser/shell_browser_main_parts.h" +#include "content/shell/browser/shell_content_browser_client.h" +#include "content/shell/browser/shell_devtools_delegate.h" +#include "content/shell/common/shell_switches.h" +#include "net/base/net_util.h" + +namespace content { + +namespace { + +// DevTools frontend path for inspector LayoutTests. +GURL GetDevToolsPathAsURL() { + base::FilePath dir_exe; + if (!PathService::Get(base::DIR_EXE, &dir_exe)) { + NOTREACHED(); + return GURL(); + } +#if defined(OS_MACOSX) + // On Mac, the executable is in + // out/Release/Content Shell.app/Contents/MacOS/Content Shell. + // We need to go up 3 directories to get to out/Release. + dir_exe = dir_exe.AppendASCII("../../.."); +#endif + base::FilePath dev_tools_path = dir_exe.AppendASCII( + "resources/inspector/devtools.html"); + return net::FilePathToFileURL(dev_tools_path); +} + +} // namespace + +// static +ShellDevToolsFrontend* ShellDevToolsFrontend::Show( + WebContents* inspected_contents) { + Shell* shell = Shell::CreateNewWindow(inspected_contents->GetBrowserContext(), + GURL(), + NULL, + MSG_ROUTING_NONE, + gfx::Size()); + ShellDevToolsFrontend* devtools_frontend = new ShellDevToolsFrontend( + shell, + DevToolsAgentHost::GetOrCreateFor(inspected_contents->GetRenderViewHost()) + .get()); + + ShellDevToolsDelegate* delegate = ShellContentBrowserClient::Get()-> + shell_browser_main_parts()->devtools_delegate(); + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) + shell->LoadURL(GetDevToolsPathAsURL()); + else + shell->LoadURL(delegate->devtools_http_handler()->GetFrontendURL(NULL)); + + return devtools_frontend; +} + +void ShellDevToolsFrontend::Focus() { + web_contents()->GetView()->Focus(); +} + +void ShellDevToolsFrontend::Close() { + frontend_shell_->Close(); +} + +ShellDevToolsFrontend::ShellDevToolsFrontend(Shell* frontend_shell, + DevToolsAgentHost* agent_host) + : WebContentsObserver(frontend_shell->web_contents()), + frontend_shell_(frontend_shell), + agent_host_(agent_host) { + frontend_host_.reset( + DevToolsClientHost::CreateDevToolsFrontendHost(web_contents(), this)); +} + +ShellDevToolsFrontend::~ShellDevToolsFrontend() { +} + +void ShellDevToolsFrontend::RenderViewCreated( + RenderViewHost* render_view_host) { + DevToolsClientHost::SetupDevToolsFrontendClient( + web_contents()->GetRenderViewHost()); + DevToolsManager* manager = DevToolsManager::GetInstance(); + manager->RegisterDevToolsClientHostFor(agent_host_.get(), + frontend_host_.get()); +} + +void ShellDevToolsFrontend::WebContentsDestroyed(WebContents* web_contents) { + DevToolsManager::GetInstance()->ClientHostClosing(frontend_host_.get()); + delete this; +} + +void ShellDevToolsFrontend::InspectedContentsClosing() { + frontend_shell_->Close(); +} + +} // namespace content diff --git a/content/shell/browser/shell_devtools_frontend.h b/content/shell/browser/shell_devtools_frontend.h new file mode 100644 index 0000000..b6a617f --- /dev/null +++ b/content/shell/browser/shell_devtools_frontend.h @@ -0,0 +1,73 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_DEVTOOLS_FRONTEND_H_ +#define CONTENT_SHELL_BROWSER_SHELL_DEVTOOLS_FRONTEND_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/devtools_agent_host.h" +#include "content/public/browser/devtools_client_host.h" +#include "content/public/browser/devtools_frontend_host_delegate.h" +#include "content/public/browser/web_contents_observer.h" + +namespace content { + +class RenderViewHost; +class Shell; +class WebContents; + +class ShellDevToolsFrontend : public WebContentsObserver, + public DevToolsFrontendHostDelegate { + public: + static ShellDevToolsFrontend* Show(WebContents* inspected_contents); + void Focus(); + void Close(); + + Shell* frontend_shell() const { return frontend_shell_; } + + private: + ShellDevToolsFrontend(Shell* frontend_shell, DevToolsAgentHost* agent_host); + virtual ~ShellDevToolsFrontend(); + + // WebContentsObserver overrides + virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE; + virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE; + + // DevToolsFrontendHostDelegate implementation + virtual void ActivateWindow() OVERRIDE {} + virtual void ChangeAttachedWindowHeight(unsigned height) OVERRIDE {} + virtual void CloseWindow() OVERRIDE {} + virtual void MoveWindow(int x, int y) OVERRIDE {} + virtual void SetDockSide(const std::string& side) OVERRIDE {} + virtual void OpenInNewTab(const std::string& url) OVERRIDE {} + virtual void SaveToFile(const std::string& url, + const std::string& content, + bool save_as) OVERRIDE {} + virtual void AppendToFile(const std::string& url, + const std::string& content) OVERRIDE {} + virtual void RequestFileSystems() OVERRIDE {} + virtual void AddFileSystem() OVERRIDE {} + virtual void RemoveFileSystem(const std::string& file_system_path) OVERRIDE {} + virtual void IndexPath(int request_id, + const std::string& file_system_path) OVERRIDE {} + virtual void StopIndexing(int request_id) OVERRIDE {} + virtual void SearchInPath(int request_id, + const std::string& file_system_path, + const std::string& query) OVERRIDE {} + + virtual void InspectedContentsClosing() OVERRIDE; + + Shell* frontend_shell_; + scoped_refptr<DevToolsAgentHost> agent_host_; + scoped_ptr<DevToolsClientHost> frontend_host_; + + DISALLOW_COPY_AND_ASSIGN(ShellDevToolsFrontend); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_DEVTOOLS_FRONTEND_H_ diff --git a/content/shell/browser/shell_download_manager_delegate.cc b/content/shell/browser/shell_download_manager_delegate.cc new file mode 100644 index 0000000..1dbbea7 --- /dev/null +++ b/content/shell/browser/shell_download_manager_delegate.cc @@ -0,0 +1,211 @@ +// 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 "content/shell/browser/shell_download_manager_delegate.h" + +#if defined(TOOLKIT_GTK) +#include <gtk/gtk.h> +#endif + +#if defined(OS_WIN) +#include <windows.h> +#include <commdlg.h> +#endif + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/download_manager.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "content/shell/browser/webkit_test_controller.h" +#include "content/shell/common/shell_switches.h" +#include "net/base/net_util.h" + +namespace content { + +ShellDownloadManagerDelegate::ShellDownloadManagerDelegate() + : download_manager_(NULL), + suppress_prompting_(false) { + // Balanced in Shutdown(); + AddRef(); +} + +ShellDownloadManagerDelegate::~ShellDownloadManagerDelegate(){ +} + + +void ShellDownloadManagerDelegate::SetDownloadManager( + DownloadManager* download_manager) { + download_manager_ = download_manager; +} + +void ShellDownloadManagerDelegate::Shutdown() { + Release(); +} + +bool ShellDownloadManagerDelegate::DetermineDownloadTarget( + DownloadItem* download, + const DownloadTargetCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // This assignment needs to be here because even at the call to + // SetDownloadManager, the system is not fully initialized. + if (default_download_path_.empty()) { + default_download_path_ = download_manager_->GetBrowserContext()->GetPath(). + Append(FILE_PATH_LITERAL("Downloads")); + } + + if (!download->GetForcedFilePath().empty()) { + callback.Run(download->GetForcedFilePath(), + DownloadItem::TARGET_DISPOSITION_OVERWRITE, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + download->GetForcedFilePath()); + return true; + } + + base::FilePath generated_name = net::GenerateFileName( + download->GetURL(), + download->GetContentDisposition(), + EmptyString(), + download->GetSuggestedFilename(), + download->GetMimeType(), + "download"); + + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + base::Bind( + &ShellDownloadManagerDelegate::GenerateFilename, + this, download->GetId(), callback, generated_name, + default_download_path_)); + return true; +} + +bool ShellDownloadManagerDelegate::ShouldOpenDownload( + DownloadItem* item, + const DownloadOpenDelayedCallback& callback) { + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree) && + WebKitTestController::Get()->IsMainWindow(item->GetWebContents()) && + item->GetMimeType() == "text/html") { + WebKitTestController::Get()->OpenURL( + net::FilePathToFileURL(item->GetFullPath())); + } + return true; +} + +void ShellDownloadManagerDelegate::GetNextId( + const DownloadIdCallback& callback) { + static uint32 next_id = DownloadItem::kInvalidId + 1; + callback.Run(next_id++); +} + +void ShellDownloadManagerDelegate::GenerateFilename( + uint32 download_id, + const DownloadTargetCallback& callback, + const base::FilePath& generated_name, + const base::FilePath& suggested_directory) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + if (!base::PathExists(suggested_directory)) + file_util::CreateDirectory(suggested_directory); + + base::FilePath suggested_path(suggested_directory.Append(generated_name)); + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind( + &ShellDownloadManagerDelegate::OnDownloadPathGenerated, + this, download_id, callback, suggested_path)); +} + +void ShellDownloadManagerDelegate::OnDownloadPathGenerated( + uint32 download_id, + const DownloadTargetCallback& callback, + const base::FilePath& suggested_path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (suppress_prompting_) { + // Testing exit. + callback.Run(suggested_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + suggested_path.AddExtension(FILE_PATH_LITERAL(".crdownload"))); + return; + } + + ChooseDownloadPath(download_id, callback, suggested_path); +} + +void ShellDownloadManagerDelegate::ChooseDownloadPath( + uint32 download_id, + const DownloadTargetCallback& callback, + const base::FilePath& suggested_path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DownloadItem* item = download_manager_->GetDownload(download_id); + if (!item || (item->GetState() != DownloadItem::IN_PROGRESS)) + return; + + base::FilePath result; +#if defined(OS_WIN) && !defined(USE_AURA) + std::wstring file_part = base::FilePath(suggested_path).BaseName().value(); + wchar_t file_name[MAX_PATH]; + base::wcslcpy(file_name, file_part.c_str(), arraysize(file_name)); + OPENFILENAME save_as; + ZeroMemory(&save_as, sizeof(save_as)); + save_as.lStructSize = sizeof(OPENFILENAME); + save_as.hwndOwner = item->GetWebContents()->GetView()->GetNativeView(); + save_as.lpstrFile = file_name; + save_as.nMaxFile = arraysize(file_name); + + std::wstring directory; + if (!suggested_path.empty()) + directory = suggested_path.DirName().value(); + + save_as.lpstrInitialDir = directory.c_str(); + save_as.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ENABLESIZING | + OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST; + + if (GetSaveFileName(&save_as)) + result = base::FilePath(std::wstring(save_as.lpstrFile)); +#elif defined(TOOLKIT_GTK) + GtkWidget *dialog; + gfx::NativeWindow parent_window; + std::string base_name = base::FilePath(suggested_path).BaseName().value(); + + parent_window = item->GetWebContents()->GetView()->GetTopLevelNativeWindow(); + dialog = gtk_file_chooser_dialog_new("Save File", + parent_window, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), + TRUE); + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), + base_name.c_str()); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *filename; + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + result = base::FilePath(filename); + g_free(filename); + } + gtk_widget_destroy(dialog); +#else + NOTIMPLEMENTED(); +#endif + + callback.Run(result, DownloadItem::TARGET_DISPOSITION_PROMPT, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, result); +} + +void ShellDownloadManagerDelegate::SetDownloadBehaviorForTesting( + const base::FilePath& default_download_path) { + default_download_path_ = default_download_path; + suppress_prompting_ = true; +} + +} // namespace content diff --git a/content/shell/browser/shell_download_manager_delegate.h b/content/shell/browser/shell_download_manager_delegate.h new file mode 100644 index 0000000..afd57e7 --- /dev/null +++ b/content/shell/browser/shell_download_manager_delegate.h @@ -0,0 +1,65 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_DOWNLOAD_MANAGER_DELEGATE_H_ +#define CONTENT_SHELL_BROWSER_SHELL_DOWNLOAD_MANAGER_DELEGATE_H_ + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "content/public/browser/download_manager_delegate.h" + +namespace content { + +class DownloadManager; + +class ShellDownloadManagerDelegate + : public DownloadManagerDelegate, + public base::RefCountedThreadSafe<ShellDownloadManagerDelegate> { + public: + ShellDownloadManagerDelegate(); + + void SetDownloadManager(DownloadManager* manager); + + virtual void Shutdown() OVERRIDE; + virtual bool DetermineDownloadTarget( + DownloadItem* download, + const DownloadTargetCallback& callback) OVERRIDE; + virtual bool ShouldOpenDownload( + DownloadItem* item, + const DownloadOpenDelayedCallback& callback) OVERRIDE; + virtual void GetNextId(const DownloadIdCallback& callback) OVERRIDE; + + // Inhibits prompting and sets the default download path. + void SetDownloadBehaviorForTesting( + const base::FilePath& default_download_path); + + protected: + // To allow subclasses for testing. + virtual ~ShellDownloadManagerDelegate(); + + private: + friend class base::RefCountedThreadSafe<ShellDownloadManagerDelegate>; + + + void GenerateFilename(uint32 download_id, + const DownloadTargetCallback& callback, + const base::FilePath& generated_name, + const base::FilePath& suggested_directory); + void OnDownloadPathGenerated(uint32 download_id, + const DownloadTargetCallback& callback, + const base::FilePath& suggested_path); + void ChooseDownloadPath(uint32 download_id, + const DownloadTargetCallback& callback, + const base::FilePath& suggested_path); + + DownloadManager* download_manager_; + base::FilePath default_download_path_; + bool suppress_prompting_; + + DISALLOW_COPY_AND_ASSIGN(ShellDownloadManagerDelegate); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_DOWNLOAD_MANAGER_DELEGATE_H_ diff --git a/content/shell/browser/shell_gtk.cc b/content/shell/browser/shell_gtk.cc new file mode 100644 index 0000000..a06a6a0 --- /dev/null +++ b/content/shell/browser/shell_gtk.cc @@ -0,0 +1,339 @@ +// 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 "content/shell/browser/shell.h" + +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> + +#include "base/logging.h" +#include "base/strings/string_piece.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/native_web_keyboard_event.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "content/public/common/renderer_preferences.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/shell/browser/shell_content_browser_client.h" + +namespace content { + +namespace { + +// Callback for Debug > Show web inspector... menu item. +gboolean ShowWebInspectorActivated(GtkWidget* widget, Shell* shell) { + shell->ShowDevTools(); + return FALSE; // Don't stop this message. +} + +GtkWidget* AddMenuEntry(GtkWidget* menu_widget, const char* text, + GCallback callback, Shell* shell) { + GtkWidget* entry = gtk_menu_item_new_with_label(text); + g_signal_connect(entry, "activate", callback, shell); + gtk_menu_shell_append(GTK_MENU_SHELL(menu_widget), entry); + return entry; +} + +GtkWidget* CreateMenu(GtkWidget* menu_bar, const char* text) { + GtkWidget* menu_widget = gtk_menu_new(); + GtkWidget* menu_header = gtk_menu_item_new_with_label(text); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_header), menu_widget); + gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), menu_header); + return menu_widget; +} + +GtkWidget* CreateMenuBar(Shell* shell) { + GtkWidget* menu_bar = gtk_menu_bar_new(); + GtkWidget* debug_menu = CreateMenu(menu_bar, "Debug"); + AddMenuEntry(debug_menu, "Show web inspector...", + G_CALLBACK(ShowWebInspectorActivated), shell); + return menu_bar; +} + +} // namespace + +void Shell::PlatformInitialize(const gfx::Size& default_window_size) { +} + +void Shell::PlatformCleanUp() { + // Nothing to clean up; GTK will clean up the widgets shortly after. +} + +void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) { + if (headless_) + return; + + GtkToolItem* item = NULL; + switch (control) { + case BACK_BUTTON: + item = back_button_; + break; + case FORWARD_BUTTON: + item = forward_button_; + break; + case STOP_BUTTON: + item = stop_button_; + break; + default: + NOTREACHED() << "Unknown UI control"; + return; + } + gtk_widget_set_sensitive(GTK_WIDGET(item), is_enabled); +} + +void Shell::PlatformSetAddressBarURL(const GURL& url) { + if (headless_) + return; + + gtk_entry_set_text(GTK_ENTRY(url_edit_view_), url.spec().c_str()); +} + +void Shell::PlatformSetIsLoading(bool loading) { + if (headless_) + return; + + if (loading) + gtk_spinner_start(GTK_SPINNER(spinner_)); + else + gtk_spinner_stop(GTK_SPINNER(spinner_)); +} + +void Shell::PlatformCreateWindow(int width, int height) { + ui_elements_height_ = 0; + if (headless_) { + SizeTo(width, height); + return; + } + + window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); + gtk_window_set_title(window_, "Content Shell"); + g_signal_connect(G_OBJECT(window_), "destroy", + G_CALLBACK(OnWindowDestroyedThunk), this); + + vbox_ = gtk_vbox_new(FALSE, 0); + + // Create the menu bar. + GtkWidget* menu_bar = CreateMenuBar(this); + gtk_box_pack_start(GTK_BOX(vbox_), menu_bar, FALSE, FALSE, 0); + + // Create the object that mediates accelerators. + GtkAccelGroup* accel_group = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW(window_), accel_group); + + // Set global window handling accelerators: + gtk_accel_group_connect( + accel_group, GDK_w, GDK_CONTROL_MASK, + GTK_ACCEL_VISIBLE, + g_cclosure_new(G_CALLBACK(OnCloseWindowKeyPressedThunk), + this, NULL)); + + gtk_accel_group_connect( + accel_group, GDK_n, GDK_CONTROL_MASK, + GTK_ACCEL_VISIBLE, + g_cclosure_new(G_CALLBACK(OnNewWindowKeyPressedThunk), + this, NULL)); + + gtk_accel_group_connect( + accel_group, GDK_F5, (GdkModifierType)0, + GTK_ACCEL_VISIBLE, + g_cclosure_new(G_CALLBACK(OnReloadKeyPressedThunk), + this, NULL)); + + GtkWidget* toolbar = gtk_toolbar_new(); + // Turn off the labels on the toolbar buttons. + gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS); + + back_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK); + g_signal_connect(back_button_, "clicked", + G_CALLBACK(&OnBackButtonClickedThunk), this); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), back_button_, -1 /* append */); + gtk_widget_add_accelerator(GTK_WIDGET(back_button_), "clicked", accel_group, + GDK_Left, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE); + + forward_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD); + g_signal_connect(forward_button_, "clicked", + G_CALLBACK(&OnForwardButtonClickedThunk), this); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), forward_button_, -1 /* append */); + gtk_widget_add_accelerator(GTK_WIDGET(forward_button_), "clicked", + accel_group, + GDK_Right, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE); + + reload_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_REFRESH); + g_signal_connect(reload_button_, "clicked", + G_CALLBACK(&OnReloadButtonClickedThunk), this); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), reload_button_, -1 /* append */); + gtk_widget_add_accelerator(GTK_WIDGET(reload_button_), "clicked", + accel_group, + GDK_r, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); + + stop_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_STOP); + g_signal_connect(stop_button_, "clicked", + G_CALLBACK(&OnStopButtonClickedThunk), this); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), stop_button_, -1 /* append */); + + url_edit_view_ = gtk_entry_new(); + g_signal_connect(G_OBJECT(url_edit_view_), "activate", + G_CALLBACK(&OnURLEntryActivateThunk), this); + + gtk_accel_group_connect( + accel_group, GDK_l, GDK_CONTROL_MASK, + GTK_ACCEL_VISIBLE, + g_cclosure_new(G_CALLBACK(OnHighlightURLViewThunk), + this, NULL)); + + GtkToolItem* tool_item = gtk_tool_item_new(); + gtk_container_add(GTK_CONTAINER(tool_item), url_edit_view_); + gtk_tool_item_set_expand(tool_item, TRUE); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_item, -1 /* append */); + + // Center a 20x20 spinner in a 26x24 area. + GtkWidget* spinner_alignment = gtk_alignment_new(0.5, 0.5, 0, 0); + gtk_alignment_set_padding(GTK_ALIGNMENT(spinner_alignment), 2, 2, 4, 4); + spinner_ = gtk_spinner_new(); + gtk_widget_set_size_request(spinner_, 20, 20); + gtk_container_add(GTK_CONTAINER(spinner_alignment), spinner_); + + spinner_item_ = gtk_tool_item_new(); + gtk_container_add(GTK_CONTAINER(spinner_item_), spinner_alignment); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), spinner_item_, -1 /* append */); + + gtk_box_pack_start(GTK_BOX(vbox_), toolbar, FALSE, FALSE, 0); + + gtk_container_add(GTK_CONTAINER(window_), vbox_); + + // Trigger layout of the UI elements, so that we can measure their + // heights. The width and height passed to this method are meant for the web + // contents view, not the top-level window. Since Gtk only seems to provide a + // suitable resizing function for top-level windows, we need to know how to + // convert from web contents view size to top-level window size. + gtk_widget_show_all(GTK_WIDGET(vbox_)); + + // Measure the heights of the UI elements, now that they have been laid out. + GtkRequisition elm_size; + gtk_widget_size_request(menu_bar, &elm_size); + ui_elements_height_ += elm_size.height; + gtk_widget_size_request(toolbar, &elm_size); + ui_elements_height_ += elm_size.height; + + // We're ready to set an initial window size. + SizeTo(width, height); + + // Finally, show the window. + gtk_widget_show_all(GTK_WIDGET(window_)); +} + +void Shell::PlatformSetContents() { + if (headless_) + return; + + WebContentsView* content_view = web_contents_->GetView(); + gtk_container_add(GTK_CONTAINER(vbox_), content_view->GetNativeView()); +} + +void Shell::SizeTo(int width, int height) { + content_width_ = width; + content_height_ = height; + + // Prefer setting the top level window's size (if we have one), rather than + // setting the inner widget's minimum size (so that the user can shrink the + // window if she wants). + if (window_) { + gtk_window_resize(window_, width, height + ui_elements_height_); + } else if (web_contents_) { + gtk_widget_set_size_request(web_contents_->GetView()->GetNativeView(), + width, height); + } +} + +void Shell::PlatformResizeSubViews() { + SizeTo(content_width_, content_height_); +} + +void Shell::Close() { + if (headless_) { + delete this; + return; + } + + gtk_widget_destroy(GTK_WIDGET(window_)); +} + +void Shell::OnBackButtonClicked(GtkWidget* widget) { + GoBackOrForward(-1); +} + +void Shell::OnForwardButtonClicked(GtkWidget* widget) { + GoBackOrForward(1); +} + +void Shell::OnReloadButtonClicked(GtkWidget* widget) { + Reload(); +} + +void Shell::OnStopButtonClicked(GtkWidget* widget) { + Stop(); +} + +void Shell::OnURLEntryActivate(GtkWidget* entry) { + const gchar* str = gtk_entry_get_text(GTK_ENTRY(entry)); + GURL url(str); + if (!url.has_scheme()) + url = GURL(std::string("http://") + std::string(str)); + LoadURL(GURL(url)); +} + +// Callback for when the main window is destroyed. +gboolean Shell::OnWindowDestroyed(GtkWidget* window) { + delete this; + return FALSE; // Don't stop this message. +} + +gboolean Shell::OnCloseWindowKeyPressed(GtkAccelGroup* accel_group, + GObject* acceleratable, + guint keyval, + GdkModifierType modifier) { + gtk_widget_destroy(GTK_WIDGET(window_)); + return TRUE; +} + +gboolean Shell::OnNewWindowKeyPressed(GtkAccelGroup* accel_group, + GObject* acceleratable, + guint keyval, + GdkModifierType modifier) { + ShellBrowserContext* browser_context = + ShellContentBrowserClient::Get()->browser_context(); + Shell::CreateNewWindow(browser_context, + GURL(), + NULL, + MSG_ROUTING_NONE, + gfx::Size()); + return TRUE; +} + +gboolean Shell::OnHighlightURLView(GtkAccelGroup* accel_group, + GObject* acceleratable, + guint keyval, + GdkModifierType modifier) { + gtk_widget_grab_focus(GTK_WIDGET(url_edit_view_)); + return TRUE; +} + +gboolean Shell::OnReloadKeyPressed(GtkAccelGroup* accel_group, + GObject* acceleratable, + guint keyval, + GdkModifierType modifier) { + Reload(); + return TRUE; +} + +void Shell::PlatformSetTitle(const string16& title) { + if (headless_) + return; + + std::string title_utf8 = UTF16ToUTF8(title); + gtk_window_set_title(GTK_WINDOW(window_), title_utf8.c_str()); +} + +} // namespace content diff --git a/content/shell/browser/shell_javascript_dialog.h b/content/shell/browser/shell_javascript_dialog.h new file mode 100644 index 0000000..4001334 --- /dev/null +++ b/content/shell/browser/shell_javascript_dialog.h @@ -0,0 +1,64 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_JAVASCRIPT_DIALOG_H_ +#define CONTENT_SHELL_BROWSER_SHELL_JAVASCRIPT_DIALOG_H_ + +#include "content/public/browser/javascript_dialog_manager.h" + +#if defined(TOOLKIT_GTK) +#include "ui/base/gtk/gtk_signal.h" +#endif + +#if defined(OS_MACOSX) +#if __OBJC__ +@class ShellJavaScriptDialogHelper; +#else +class ShellJavaScriptDialogHelper; +#endif // __OBJC__ +#endif // defined(OS_MACOSX) + +namespace content { + +class ShellJavaScriptDialogManager; + +class ShellJavaScriptDialog { + public: + ShellJavaScriptDialog( + ShellJavaScriptDialogManager* manager, + gfx::NativeWindow parent_window, + JavaScriptMessageType message_type, + const string16& message_text, + const string16& default_prompt_text, + const JavaScriptDialogManager::DialogClosedCallback& callback); + ~ShellJavaScriptDialog(); + + // Called to cancel a dialog mid-flight. + void Cancel(); + + private: + ShellJavaScriptDialogManager* manager_; + JavaScriptDialogManager::DialogClosedCallback callback_; + +#if defined(OS_MACOSX) + ShellJavaScriptDialogHelper* helper_; // owned +#elif defined(OS_WIN) + JavaScriptMessageType message_type_; + HWND dialog_win_; + string16 message_text_; + string16 default_prompt_text_; + static INT_PTR CALLBACK DialogProc(HWND dialog, UINT message, WPARAM wparam, + LPARAM lparam); +#elif defined(TOOLKIT_GTK) + GtkWidget* gtk_dialog_; + gfx::NativeWindow parent_window_; + CHROMEGTK_CALLBACK_1(ShellJavaScriptDialog, void, OnResponse, int); +#endif + + DISALLOW_COPY_AND_ASSIGN(ShellJavaScriptDialog); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_JAVASCRIPT_DIALOG_H_ diff --git a/content/shell/browser/shell_javascript_dialog_gtk.cc b/content/shell/browser/shell_javascript_dialog_gtk.cc new file mode 100644 index 0000000..87bd6f3 --- /dev/null +++ b/content/shell/browser/shell_javascript_dialog_gtk.cc @@ -0,0 +1,127 @@ +// 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 "content/shell/browser/shell_javascript_dialog.h" + +#include <gtk/gtk.h> + +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/shell/app/resource.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_javascript_dialog_manager.h" + +namespace { + +const char kPromptTextId[] = "content_shell_prompt_text"; + +// If there's a text entry in the dialog, get the text from the first one and +// return it. +string16 GetPromptText(GtkDialog* dialog) { + GtkWidget* widget = static_cast<GtkWidget*>( + g_object_get_data(G_OBJECT(dialog), kPromptTextId)); + if (widget) + return UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(widget))); + return string16(); +} + +} // namespace + + +namespace content { + +ShellJavaScriptDialog::ShellJavaScriptDialog( + ShellJavaScriptDialogManager* manager, + gfx::NativeWindow parent_window, + JavaScriptMessageType message_type, + const string16& message_text, + const string16& default_prompt_text, + const JavaScriptDialogManager::DialogClosedCallback& callback) + : manager_(manager), + callback_(callback), + parent_window_(parent_window) { + GtkButtonsType buttons = GTK_BUTTONS_NONE; + GtkMessageType gtk_message_type = GTK_MESSAGE_OTHER; + + switch (message_type) { + case content::JAVASCRIPT_MESSAGE_TYPE_ALERT: + buttons = GTK_BUTTONS_NONE; + gtk_message_type = GTK_MESSAGE_WARNING; + break; + + case content::JAVASCRIPT_MESSAGE_TYPE_CONFIRM: + buttons = GTK_BUTTONS_CANCEL; + gtk_message_type = GTK_MESSAGE_QUESTION; + break; + + case content::JAVASCRIPT_MESSAGE_TYPE_PROMPT: + buttons = GTK_BUTTONS_CANCEL; + gtk_message_type = GTK_MESSAGE_QUESTION; + break; + + default: + NOTREACHED(); + } + + gtk_dialog_ = gtk_message_dialog_new(parent_window_, + GTK_DIALOG_MODAL, + gtk_message_type, + buttons, + "%s", + UTF16ToUTF8(message_text).c_str()); + g_signal_connect(gtk_dialog_, + "delete-event", + G_CALLBACK(gtk_widget_hide_on_delete), + NULL); + gtk_window_set_title(GTK_WINDOW(gtk_dialog_), "JavaScript"); + + GtkWidget* ok_button = gtk_dialog_add_button(GTK_DIALOG(gtk_dialog_), + GTK_STOCK_OK, + GTK_RESPONSE_OK); + + if (message_type != content::JAVASCRIPT_MESSAGE_TYPE_PROMPT) + gtk_widget_grab_focus(ok_button); + + if (message_type == content::JAVASCRIPT_MESSAGE_TYPE_PROMPT) { + GtkWidget* content_area = + gtk_dialog_get_content_area(GTK_DIALOG(gtk_dialog_)); + GtkWidget* text_box = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(text_box), + UTF16ToUTF8(default_prompt_text).c_str()); + gtk_box_pack_start(GTK_BOX(content_area), text_box, TRUE, TRUE, 0); + g_object_set_data(G_OBJECT(gtk_dialog_), kPromptTextId, text_box); + gtk_entry_set_activates_default(GTK_ENTRY(text_box), TRUE); + } + + gtk_dialog_set_default_response(GTK_DIALOG(gtk_dialog_), GTK_RESPONSE_OK); + g_signal_connect(gtk_dialog_, "response", G_CALLBACK(OnResponseThunk), this); + gtk_widget_show_all(GTK_WIDGET(gtk_dialog_)); +} + +ShellJavaScriptDialog::~ShellJavaScriptDialog() { +} + +void ShellJavaScriptDialog::Cancel() { +} + +void ShellJavaScriptDialog::OnResponse(GtkWidget* dialog, int response_id) { + switch (response_id) { + case GTK_RESPONSE_OK: + callback_.Run(true, GetPromptText(GTK_DIALOG(dialog))); + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_DELETE_EVENT: + callback_.Run(false, string16()); + break; + default: + NOTREACHED(); + } + + gtk_widget_destroy(dialog); + + manager_->DialogClosed(this); +} + +} // namespace content diff --git a/content/shell/browser/shell_javascript_dialog_mac.mm b/content/shell/browser/shell_javascript_dialog_mac.mm new file mode 100644 index 0000000..fe9de98 --- /dev/null +++ b/content/shell/browser/shell_javascript_dialog_mac.mm @@ -0,0 +1,138 @@ +// 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 "content/shell/browser/shell_javascript_dialog.h" + +#import <Cocoa/Cocoa.h> + +#import "base/mac/scoped_nsobject.h" +#include "base/strings/sys_string_conversions.h" +#include "content/shell/browser/shell_javascript_dialog_manager.h" + +// Helper object that receives the notification that the dialog/sheet is +// going away. Is responsible for cleaning itself up. +@interface ShellJavaScriptDialogHelper : NSObject<NSAlertDelegate> { + @private + base::scoped_nsobject<NSAlert> alert_; + NSTextField* textField_; // WEAK; owned by alert_ + + // Copies of the fields in ShellJavaScriptDialog because they're private. + content::ShellJavaScriptDialogManager* manager_; + content::JavaScriptDialogManager::DialogClosedCallback callback_; +} + +- (id)initHelperWithManager:(content::ShellJavaScriptDialogManager*)manager + andCallback:(content::JavaScriptDialogManager::DialogClosedCallback)callback; +- (NSAlert*)alert; +- (NSTextField*)textField; +- (void)alertDidEnd:(NSAlert*)alert + returnCode:(int)returnCode + contextInfo:(void*)contextInfo; +- (void)cancel; + +@end + +@implementation ShellJavaScriptDialogHelper + +- (id)initHelperWithManager:(content::ShellJavaScriptDialogManager*)manager + andCallback:(content::JavaScriptDialogManager::DialogClosedCallback)callback { + if (self = [super init]) { + manager_ = manager; + callback_ = callback; + } + + return self; +} + +- (NSAlert*)alert { + alert_.reset([[NSAlert alloc] init]); + return alert_; +} + +- (NSTextField*)textField { + textField_ = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 300, 22)]; + [[textField_ cell] setLineBreakMode:NSLineBreakByTruncatingTail]; + [alert_ setAccessoryView:textField_]; + [textField_ release]; + + return textField_; +} + +- (void)alertDidEnd:(NSAlert*)alert + returnCode:(int)returnCode + contextInfo:(void*)contextInfo { + if (returnCode == NSRunStoppedResponse) + return; + + bool success = returnCode == NSAlertFirstButtonReturn; + string16 input; + if (textField_) + input = base::SysNSStringToUTF16([textField_ stringValue]); + + content::ShellJavaScriptDialog* native_dialog = + reinterpret_cast<content::ShellJavaScriptDialog*>(contextInfo); + callback_.Run(success, input); + manager_->DialogClosed(native_dialog); +} + +- (void)cancel { + [NSApp endSheet:[alert_ window]]; + alert_.reset(); +} + +@end + +namespace content { + +ShellJavaScriptDialog::ShellJavaScriptDialog( + ShellJavaScriptDialogManager* manager, + gfx::NativeWindow parent_window, + JavaScriptMessageType message_type, + const string16& message_text, + const string16& default_prompt_text, + const JavaScriptDialogManager::DialogClosedCallback& callback) + : manager_(manager), + callback_(callback) { + bool text_field = message_type == JAVASCRIPT_MESSAGE_TYPE_PROMPT; + bool one_button = message_type == JAVASCRIPT_MESSAGE_TYPE_ALERT; + + helper_ = + [[ShellJavaScriptDialogHelper alloc] initHelperWithManager:manager + andCallback:callback]; + + // Show the modal dialog. + NSAlert* alert = [helper_ alert]; + NSTextField* field = nil; + if (text_field) { + field = [helper_ textField]; + [field setStringValue:base::SysUTF16ToNSString(default_prompt_text)]; + } + [alert setDelegate:helper_]; + [alert setInformativeText:base::SysUTF16ToNSString(message_text)]; + [alert setMessageText:@"Javascript alert"]; + [alert addButtonWithTitle:@"OK"]; + if (!one_button) { + NSButton* other = [alert addButtonWithTitle:@"Cancel"]; + [other setKeyEquivalent:@"\e"]; + } + + [alert + beginSheetModalForWindow:nil // nil here makes it app-modal + modalDelegate:helper_ + didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) + contextInfo:this]; + + if ([alert accessoryView]) + [[alert window] makeFirstResponder:[alert accessoryView]]; +} + +ShellJavaScriptDialog::~ShellJavaScriptDialog() { + [helper_ release]; +} + +void ShellJavaScriptDialog::Cancel() { + [helper_ cancel]; +} + +} // namespace content diff --git a/content/shell/browser/shell_javascript_dialog_manager.cc b/content/shell/browser/shell_javascript_dialog_manager.cc new file mode 100644 index 0000000..1476f7c --- /dev/null +++ b/content/shell/browser/shell_javascript_dialog_manager.cc @@ -0,0 +1,143 @@ +// 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 "content/shell/browser/shell_javascript_dialog_manager.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "content/shell/browser/shell_javascript_dialog.h" +#include "content/shell/browser/webkit_test_controller.h" +#include "content/shell/common/shell_switches.h" +#include "net/base/net_util.h" + +namespace content { + +ShellJavaScriptDialogManager::ShellJavaScriptDialogManager() { +} + +ShellJavaScriptDialogManager::~ShellJavaScriptDialogManager() { +} + +void ShellJavaScriptDialogManager::RunJavaScriptDialog( + WebContents* web_contents, + const GURL& origin_url, + const std::string& accept_lang, + JavaScriptMessageType javascript_message_type, + const string16& message_text, + const string16& default_prompt_text, + const DialogClosedCallback& callback, + bool* did_suppress_message) { + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) { + callback.Run(true, string16()); + return; + } + + if (!dialog_request_callback_.is_null()) { + dialog_request_callback_.Run(); + callback.Run(true, string16()); + dialog_request_callback_.Reset(); + return; + } + +#if defined(OS_MACOSX) || defined(OS_WIN) || defined(TOOLKIT_GTK) + *did_suppress_message = false; + + if (dialog_) { + // One dialog at a time, please. + *did_suppress_message = true; + return; + } + + string16 new_message_text = net::FormatUrl(origin_url, accept_lang) + + ASCIIToUTF16("\n\n") + + message_text; + gfx::NativeWindow parent_window = + web_contents->GetView()->GetTopLevelNativeWindow(); + + dialog_.reset(new ShellJavaScriptDialog(this, + parent_window, + javascript_message_type, + new_message_text, + default_prompt_text, + callback)); +#else + // TODO: implement ShellJavaScriptDialog for other platforms, drop this #if + *did_suppress_message = true; + return; +#endif +} + +void ShellJavaScriptDialogManager::RunBeforeUnloadDialog( + WebContents* web_contents, + const string16& message_text, + bool is_reload, + const DialogClosedCallback& callback) { + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) { + callback.Run(true, string16()); + return; + } + + if (!dialog_request_callback_.is_null()) { + dialog_request_callback_.Run(); + callback.Run(true, string16()); + dialog_request_callback_.Reset(); + return; + } + +#if defined(OS_MACOSX) || defined(OS_WIN) || defined(TOOLKIT_GTK) + if (dialog_) { + // Seriously!? + callback.Run(true, string16()); + return; + } + + string16 new_message_text = + message_text + + ASCIIToUTF16("\n\nIs it OK to leave/reload this page?"); + + gfx::NativeWindow parent_window = + web_contents->GetView()->GetTopLevelNativeWindow(); + + dialog_.reset(new ShellJavaScriptDialog(this, + parent_window, + JAVASCRIPT_MESSAGE_TYPE_CONFIRM, + new_message_text, + string16(), // default_prompt_text + callback)); +#else + // TODO: implement ShellJavaScriptDialog for other platforms, drop this #if + callback.Run(true, string16()); + return; +#endif +} + +void ShellJavaScriptDialogManager::CancelActiveAndPendingDialogs( + WebContents* web_contents) { +#if defined(OS_MACOSX) || defined(OS_WIN) || defined(TOOLKIT_GTK) + if (dialog_) { + dialog_->Cancel(); + dialog_.reset(); + } +#else + // TODO: implement ShellJavaScriptDialog for other platforms, drop this #if +#endif +} + +void ShellJavaScriptDialogManager::WebContentsDestroyed( + WebContents* web_contents) { +} + +void ShellJavaScriptDialogManager::DialogClosed(ShellJavaScriptDialog* dialog) { +#if defined(OS_MACOSX) || defined(OS_WIN) || defined(TOOLKIT_GTK) + DCHECK_EQ(dialog, dialog_.get()); + dialog_.reset(); +#else + // TODO: implement ShellJavaScriptDialog for other platforms, drop this #if +#endif +} + +} // namespace content diff --git a/content/shell/browser/shell_javascript_dialog_manager.h b/content/shell/browser/shell_javascript_dialog_manager.h new file mode 100644 index 0000000..763736f --- /dev/null +++ b/content/shell/browser/shell_javascript_dialog_manager.h @@ -0,0 +1,67 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_JAVASCRIPT_DIALOG_MANAGER_H_ +#define CONTENT_SHELL_BROWSER_SHELL_JAVASCRIPT_DIALOG_MANAGER_H_ + +#include "base/callback_forward.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/javascript_dialog_manager.h" + +namespace content { + +class ShellJavaScriptDialog; + +class ShellJavaScriptDialogManager : public JavaScriptDialogManager { + public: + ShellJavaScriptDialogManager(); + virtual ~ShellJavaScriptDialogManager(); + + // JavaScriptDialogManager: + virtual void RunJavaScriptDialog( + WebContents* web_contents, + const GURL& origin_url, + const std::string& accept_lang, + JavaScriptMessageType javascript_message_type, + const string16& message_text, + const string16& default_prompt_text, + const DialogClosedCallback& callback, + bool* did_suppress_message) OVERRIDE; + + virtual void RunBeforeUnloadDialog( + WebContents* web_contents, + const string16& message_text, + bool is_reload, + const DialogClosedCallback& callback) OVERRIDE; + + virtual void CancelActiveAndPendingDialogs( + WebContents* web_contents) OVERRIDE; + + virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE; + + // Called by the ShellJavaScriptDialog when it closes. + void DialogClosed(ShellJavaScriptDialog* dialog); + + // Used for content_browsertests. + void set_dialog_request_callback(const base::Closure& callback) { + dialog_request_callback_ = callback; + } + + private: +#if defined(OS_MACOSX) || defined(OS_WIN) || defined(TOOLKIT_GTK) + // The dialog being shown. No queueing. + scoped_ptr<ShellJavaScriptDialog> dialog_; +#else + // TODO: implement ShellJavaScriptDialog for other platforms, drop this #if +#endif + + base::Closure dialog_request_callback_; + + DISALLOW_COPY_AND_ASSIGN(ShellJavaScriptDialogManager); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_JAVASCRIPT_DIALOG_MANAGER_H_ diff --git a/content/shell/browser/shell_javascript_dialog_win.cc b/content/shell/browser/shell_javascript_dialog_win.cc new file mode 100644 index 0000000..a59639e --- /dev/null +++ b/content/shell/browser/shell_javascript_dialog_win.cc @@ -0,0 +1,113 @@ +// 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 "content/shell/browser/shell_javascript_dialog.h" + +#include "base/strings/string_util.h" +#include "content/shell/app/resource.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_javascript_dialog_manager.h" + +namespace content { + +class ShellJavaScriptDialog; + +INT_PTR CALLBACK ShellJavaScriptDialog::DialogProc(HWND dialog, + UINT message, + WPARAM wparam, + LPARAM lparam) { + switch (message) { + case WM_INITDIALOG: { + SetWindowLongPtr(dialog, DWLP_USER, static_cast<LONG_PTR>(lparam)); + ShellJavaScriptDialog* owner = + reinterpret_cast<ShellJavaScriptDialog*>(lparam); + owner->dialog_win_ = dialog; + SetDlgItemText(dialog, IDC_DIALOGTEXT, owner->message_text_.c_str()); + if (owner->message_type_ == JAVASCRIPT_MESSAGE_TYPE_PROMPT) + SetDlgItemText(dialog, IDC_PROMPTEDIT, + owner->default_prompt_text_.c_str()); + break; + } + case WM_DESTROY: { + ShellJavaScriptDialog* owner = reinterpret_cast<ShellJavaScriptDialog*>( + GetWindowLongPtr(dialog, DWLP_USER)); + if (owner->dialog_win_) { + owner->dialog_win_ = 0; + owner->callback_.Run(false, string16()); + owner->manager_->DialogClosed(owner); + } + break; + } + case WM_COMMAND: { + ShellJavaScriptDialog* owner = reinterpret_cast<ShellJavaScriptDialog*>( + GetWindowLongPtr(dialog, DWLP_USER)); + string16 user_input; + bool finish = false; + bool result; + switch (LOWORD(wparam)) { + case IDOK: + finish = true; + result = true; + if (owner->message_type_ == JAVASCRIPT_MESSAGE_TYPE_PROMPT) { + int length = + GetWindowTextLength(GetDlgItem(dialog, IDC_PROMPTEDIT)) + 1; + GetDlgItemText(dialog, IDC_PROMPTEDIT, + WriteInto(&user_input, length), length); + } + break; + case IDCANCEL: + finish = true; + result = false; + break; + } + if (finish) { + owner->dialog_win_ = 0; + owner->callback_.Run(result, user_input); + DestroyWindow(dialog); + owner->manager_->DialogClosed(owner); + } + break; + } + default: + return DefWindowProc(dialog, message, wparam, lparam); + } + return 0; +} + +ShellJavaScriptDialog::ShellJavaScriptDialog( + ShellJavaScriptDialogManager* manager, + gfx::NativeWindow parent_window, + JavaScriptMessageType message_type, + const string16& message_text, + const string16& default_prompt_text, + const JavaScriptDialogManager::DialogClosedCallback& callback) + : manager_(manager), + callback_(callback), + message_text_(message_text), + default_prompt_text_(default_prompt_text), + message_type_(message_type) { + int dialog_type; + if (message_type == JAVASCRIPT_MESSAGE_TYPE_ALERT) + dialog_type = IDD_ALERT; + else if (message_type == JAVASCRIPT_MESSAGE_TYPE_CONFIRM) + dialog_type = IDD_CONFIRM; + else // JAVASCRIPT_MESSAGE_TYPE_PROMPT + dialog_type = IDD_PROMPT; + + dialog_win_ = CreateDialogParam(GetModuleHandle(0), + MAKEINTRESOURCE(dialog_type), 0, DialogProc, + reinterpret_cast<LPARAM>(this)); + ShowWindow(dialog_win_, SW_SHOWNORMAL); +} + +ShellJavaScriptDialog::~ShellJavaScriptDialog() { + Cancel(); +} + +void ShellJavaScriptDialog::Cancel() { + if (dialog_win_) + DestroyWindow(dialog_win_); +} + +} // namespace content diff --git a/content/shell/browser/shell_layout_tests_android.cc b/content/shell/browser/shell_layout_tests_android.cc new file mode 100644 index 0000000..de94b8c --- /dev/null +++ b/content/shell/browser/shell_layout_tests_android.cc @@ -0,0 +1,93 @@ +// 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 "content/shell/browser/shell_layout_tests_android.h" + +#include "base/android/fifo_utils.h" +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/message_loop/message_loop.h" +#include "content/public/test/nested_message_pump_android.h" +#include "content/shell/common/shell_switches.h" +#include "jni/ShellLayoutTestUtils_jni.h" +#include "url/gurl.h" + +namespace { + +// Path to search for when translating a layout test path to an URL. +const char kAndroidLayoutTestPath[] = + "/data/local/tmp/third_party/WebKit/LayoutTests/"; + +// The base URL from which layout tests are being served on Android. +const char kAndroidLayoutTestBase[] = "http://127.0.0.1:8000/all-tests/"; + +base::FilePath GetTestFilesDirectory(JNIEnv* env) { + ScopedJavaLocalRef<jstring> directory = + content::Java_ShellLayoutTestUtils_getApplicationFilesDirectory( + env, base::android::GetApplicationContext()); + return base::FilePath(ConvertJavaStringToUTF8(directory)); +} + +void EnsureCreateFIFO(const base::FilePath& path) { + unlink(path.value().c_str()); + CHECK(base::android::CreateFIFO(path, 0666)) + << "Unable to create the Android's FIFO: " << path.value().c_str(); +} + +base::MessagePump* CreateMessagePumpForUI() { + return new content::NestedMessagePumpAndroid(); +} + +} // namespace + +namespace content { + +bool GetTestUrlForAndroid(std::string& path_or_url, GURL* url) { + if (path_or_url.find(kAndroidLayoutTestPath) == std::string::npos) + return false; + + std::string test_location(kAndroidLayoutTestBase); + test_location.append(path_or_url.substr(strlen(kAndroidLayoutTestPath))); + + *url = GURL(test_location); + return true; +} + +void EnsureInitializeForAndroidLayoutTests() { + CHECK(CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)); + + JNIEnv* env = base::android::AttachCurrentThread(); + content::NestedMessagePumpAndroid::RegisterJni(env); + content::RegisterNativesImpl(env); + + bool success = base::MessageLoop::InitMessagePumpForUIFactory( + &CreateMessagePumpForUI); + CHECK(success) << "Unable to initialize the message pump for Android."; + + // Android will need three FIFOs to communicate with the Blink test runner, + // one for each of [stdout, stderr, stdin]. + base::FilePath files_dir(GetTestFilesDirectory(env)); + + base::FilePath stdout_fifo(files_dir.Append(FILE_PATH_LITERAL("test.fifo"))); + EnsureCreateFIFO(stdout_fifo); + + base::FilePath stderr_fifo( + files_dir.Append(FILE_PATH_LITERAL("stderr.fifo"))); + EnsureCreateFIFO(stderr_fifo); + + base::FilePath stdin_fifo(files_dir.Append(FILE_PATH_LITERAL("stdin.fifo"))); + EnsureCreateFIFO(stdin_fifo); + + // Redirecting stdout needs to happen before redirecting stdin, which needs + // to happen before redirecting stderr. + success = base::android::RedirectStream(stdout, stdout_fifo, "w") && + base::android::RedirectStream(stdin, stdin_fifo, "r") && + base::android::RedirectStream(stderr, stderr_fifo, "w"); + + CHECK(success) << "Unable to initialize the Android FIFOs."; +} + +} // namespace content diff --git a/content/shell/browser/shell_layout_tests_android.h b/content/shell/browser/shell_layout_tests_android.h new file mode 100644 index 0000000..7410954 --- /dev/null +++ b/content/shell/browser/shell_layout_tests_android.h @@ -0,0 +1,25 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_LAYOUT_TESTS_ANDROID_H_ +#define CONTENT_SHELL_BROWSER_SHELL_LAYOUT_TESTS_ANDROID_H_ + +#include <string> + +class GURL; + +namespace content { + +// On Android, all passed tests will be paths to a local temporary directory. +// However, because we can't transfer all test files to the device, translate +// those paths to a local, forwarded URL so the host can serve them. +bool GetTestUrlForAndroid(std::string& path_or_url, GURL* url); + +// Initialize the nested message loop and FIFOs for Android, and verify that +// all has been set up using a few appropriate CHECK()s. +void EnsureInitializeForAndroidLayoutTests(); + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_LAYOUT_TESTS_ANDROID_H_ diff --git a/content/shell/browser/shell_login_dialog.cc b/content/shell/browser/shell_login_dialog.cc new file mode 100644 index 0000000..f1639e9 --- /dev/null +++ b/content/shell/browser/shell_login_dialog.cc @@ -0,0 +1,107 @@ +// 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 "content/shell/browser/shell_login_dialog.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/resource_dispatcher_host.h" +#include "net/base/auth.h" +#include "net/url_request/url_request.h" +#include "ui/base/text/text_elider.h" + +namespace content { + +ShellLoginDialog::ShellLoginDialog( + net::AuthChallengeInfo* auth_info, + net::URLRequest* request) : auth_info_(auth_info), + request_(request) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ShellLoginDialog::PrepDialog, this, + ASCIIToUTF16(auth_info->challenger.ToString()), + UTF8ToUTF16(auth_info->realm))); +} + +void ShellLoginDialog::OnRequestCancelled() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ShellLoginDialog::PlatformRequestCancelled, this)); +} + +void ShellLoginDialog::UserAcceptedAuth(const string16& username, + const string16& password) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&ShellLoginDialog::SendAuthToRequester, this, + true, username, password)); +} + +void ShellLoginDialog::UserCancelledAuth() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&ShellLoginDialog::SendAuthToRequester, this, + false, string16(), string16())); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ShellLoginDialog::PlatformCleanUp, this)); +} + +ShellLoginDialog::~ShellLoginDialog() { + // Cannot post any tasks here; this object is going away and cannot be + // referenced/dereferenced. +} + +#if !defined(OS_MACOSX) && !defined(TOOLKIT_GTK) +// Bogus implementations for linking. They are never called because +// ResourceDispatcherHostDelegate::CreateLoginDelegate returns NULL. +// TODO: implement ShellLoginDialog for other platforms, drop this #if +void ShellLoginDialog::PlatformCreateDialog(const string16& message) {} +void ShellLoginDialog::PlatformCleanUp() {} +void ShellLoginDialog::PlatformRequestCancelled() {} +#endif + +void ShellLoginDialog::PrepDialog(const string16& host, + const string16& realm) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // The realm is controlled by the remote server, so there is no reason to + // believe it is of a reasonable length. + string16 elided_realm; + ui::ElideString(realm, 120, &elided_realm); + + string16 explanation = + ASCIIToUTF16("The server ") + host + + ASCIIToUTF16(" requires a username and password."); + + if (!elided_realm.empty()) { + explanation += ASCIIToUTF16(" The server says: "); + explanation += elided_realm; + explanation += ASCIIToUTF16("."); + } + + PlatformCreateDialog(explanation); +} + +void ShellLoginDialog::SendAuthToRequester(bool success, + const string16& username, + const string16& password) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (success) + request_->SetAuth(net::AuthCredentials(username, password)); + else + request_->CancelAuth(); + ResourceDispatcherHost::Get()->ClearLoginDelegateForRequest(request_); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ShellLoginDialog::PlatformCleanUp, this)); +} + +} // namespace content diff --git a/content/shell/browser/shell_login_dialog.h b/content/shell/browser/shell_login_dialog.h new file mode 100644 index 0000000..d9b0504 --- /dev/null +++ b/content/shell/browser/shell_login_dialog.h @@ -0,0 +1,97 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_LOGIN_DIALOG_H_ +#define CONTENT_SHELL_BROWSER_SHELL_LOGIN_DIALOG_H_ + +#include "base/compiler_specific.h" +#include "base/strings/string16.h" +#include "content/public/browser/resource_dispatcher_host_login_delegate.h" + +#if defined(TOOLKIT_GTK) +#include "ui/base/gtk/gtk_signal.h" +#endif + +#if defined(OS_MACOSX) +#if __OBJC__ +@class ShellLoginDialogHelper; +#else +class ShellLoginDialogHelper; +#endif // __OBJC__ +#endif // defined(OS_MACOSX) + +namespace net { +class AuthChallengeInfo; +class URLRequest; +} + +namespace content { + +// This class provides a dialog box to ask the user for credentials. Useful in +// ResourceDispatcherHostDelegate::CreateLoginDelegate. +class ShellLoginDialog : public ResourceDispatcherHostLoginDelegate { + public: + // Threading: IO thread. + ShellLoginDialog(net::AuthChallengeInfo* auth_info, net::URLRequest* request); + + // ResourceDispatcherHostLoginDelegate implementation: + // Threading: IO thread. + virtual void OnRequestCancelled() OVERRIDE; + + // Called by the platform specific code when the user responds. Public because + // the aforementioned platform specific code may not have access to private + // members. Not to be called from client code. + // Threading: UI thread. + void UserAcceptedAuth(const string16& username, const string16& password); + void UserCancelledAuth(); + + protected: + // Threading: any + virtual ~ShellLoginDialog(); + + private: + // All the methods that begin with Platform need to be implemented by the + // platform specific LoginDialog implementation. + // Creates the dialog. + // Threading: UI thread. + void PlatformCreateDialog(const string16& message); + // Called from the destructor to let each platform do any necessary cleanup. + // Threading: UI thread. + void PlatformCleanUp(); + // Called from OnRequestCancelled if the request was cancelled. + // Threading: UI thread. + void PlatformRequestCancelled(); + + // Sets up dialog creation. + // Threading: UI thread. + void PrepDialog(const string16& host, const string16& realm); + + // Sends the authentication to the requester. + // Threading: IO thread. + void SendAuthToRequester(bool success, + const string16& username, + const string16& password); + + // Who/where/what asked for the authentication. + // Threading: IO thread. + scoped_refptr<net::AuthChallengeInfo> auth_info_; + + // The request that wants login data. + // Threading: IO thread. + net::URLRequest* request_; + +#if defined(OS_MACOSX) + // Threading: UI thread. + ShellLoginDialogHelper* helper_; // owned +#elif defined(TOOLKIT_GTK) + GtkWidget* username_entry_; + GtkWidget* password_entry_; + GtkWidget* root_; + CHROMEGTK_CALLBACK_1(ShellLoginDialog, void, OnResponse, int); +#endif +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_LOGIN_DIALOG_H_ diff --git a/content/shell/browser/shell_login_dialog_gtk.cc b/content/shell/browser/shell_login_dialog_gtk.cc new file mode 100644 index 0000000..b08b449 --- /dev/null +++ b/content/shell/browser/shell_login_dialog_gtk.cc @@ -0,0 +1,111 @@ +// 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 "content/shell/browser/shell_login_dialog.h" + +#include <gtk/gtk.h> + +#include "base/logging.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/resource_dispatcher_host.h" +#include "content/public/browser/resource_request_info.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "ui/base/gtk/gtk_hig_constants.h" + +namespace content { + +void ShellLoginDialog::PlatformCreateDialog(const string16& message) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + int render_process_id; + int render_view_id; + if (!ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderView( + &render_process_id, &render_view_id)) { + NOTREACHED(); + } + + WebContents* web_contents = NULL; + RenderViewHost* render_view_host = + RenderViewHost::FromID(render_process_id, render_view_id); + if (render_view_host) + web_contents = WebContents::FromRenderViewHost(render_view_host); + DCHECK(web_contents); + + gfx::NativeWindow parent_window = + web_contents->GetView()->GetTopLevelNativeWindow(); + + root_ = gtk_message_dialog_new(parent_window, + GTK_DIALOG_MODAL, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK_CANCEL, + "Please log in."); + + GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(root_)); + GtkWidget* label = gtk_label_new(UTF16ToUTF8(message).c_str()); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_box_pack_start(GTK_BOX(content_area), label, FALSE, FALSE, 0); + + username_entry_ = gtk_entry_new(); + gtk_entry_set_activates_default(GTK_ENTRY(username_entry_), TRUE); + + password_entry_ = gtk_entry_new(); + gtk_entry_set_activates_default(GTK_ENTRY(password_entry_), TRUE); + gtk_entry_set_visibility(GTK_ENTRY(password_entry_), FALSE); + + GtkWidget* table = gtk_table_new(2, 2, FALSE); + gtk_table_set_col_spacing(GTK_TABLE(table), 0, ui::kLabelSpacing); + gtk_table_set_row_spacings(GTK_TABLE(table), ui::kControlSpacing); + + GtkWidget* username_label = gtk_label_new("Username:"); + gtk_misc_set_alignment(GTK_MISC(username_label), 0, 0.5); + + gtk_table_attach(GTK_TABLE(table), username_label, 0, 1, 0, 1, GTK_FILL, + GTK_FILL, 0, 0); + gtk_table_attach_defaults(GTK_TABLE(table), username_entry_, 1, 2, 0, 1); + + GtkWidget* password_label = gtk_label_new("Password:"); + gtk_misc_set_alignment(GTK_MISC(password_label), 0, 0.5); + + gtk_table_attach(GTK_TABLE(table), password_label, 0, 1, 1, 2, GTK_FILL, + GTK_FILL, 0, 0); + gtk_table_attach_defaults(GTK_TABLE(table), password_entry_, 1, 2, 1, 2); + + gtk_box_pack_start(GTK_BOX(content_area), table, FALSE, FALSE, 0); + + g_signal_connect(root_, "response", G_CALLBACK(OnResponseThunk), this); + gtk_widget_grab_focus(username_entry_); + gtk_widget_show_all(GTK_WIDGET(root_)); +} + +void ShellLoginDialog::PlatformCleanUp() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +void ShellLoginDialog::PlatformRequestCancelled() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +void ShellLoginDialog::OnResponse(GtkWidget* sender, int response_id) { + switch (response_id) { + case GTK_RESPONSE_OK: + UserAcceptedAuth( + UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(username_entry_))), + UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(password_entry_)))); + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_DELETE_EVENT: + UserCancelledAuth(); + break; + default: + NOTREACHED(); + } + + gtk_widget_destroy(root_); +} + +} // namespace content diff --git a/content/shell/browser/shell_login_dialog_mac.mm b/content/shell/browser/shell_login_dialog_mac.mm new file mode 100644 index 0000000..447aa58 --- /dev/null +++ b/content/shell/browser/shell_login_dialog_mac.mm @@ -0,0 +1,122 @@ +// 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 "content/shell/browser/shell_login_dialog.h" + +#import <Cocoa/Cocoa.h> + +#include "base/logging.h" +#include "base/mac/bundle_locations.h" +#import "base/mac/scoped_nsobject.h" +#include "base/strings/sys_string_conversions.h" +#include "content/public/browser/browser_thread.h" +#import "ui/base/cocoa/nib_loading.h" + +namespace { + +const int kUsernameFieldTag = 1; +const int kPasswordFieldTag = 2; + +} // namespace + +// Helper object that receives the notification that the dialog/sheet is +// going away. +@interface ShellLoginDialogHelper : NSObject<NSAlertDelegate> { + @private + base::scoped_nsobject<NSAlert> alert_; + NSTextField* usernameField_; // WEAK; owned by alert_ + NSSecureTextField* passwordField_; // WEAK; owned by alert_ +} + +- (NSAlert*)alert; +- (NSView*)accessoryView; +- (void)focus; +- (void)alertDidEnd:(NSAlert*)alert + returnCode:(int)returnCode + contextInfo:(void*)contextInfo; +- (void)cancel; + +@end + +@implementation ShellLoginDialogHelper + +- (NSAlert*)alert { + alert_.reset([[NSAlert alloc] init]); + [alert_ setAccessoryView:[self accessoryView]]; + return alert_; +} + +- (NSView*)accessoryView { + NSView* accessory_view = ui::GetViewFromNib(@"HttpAuth"); + if (!accessory_view) + return nil; + + usernameField_ = [accessory_view viewWithTag:kUsernameFieldTag]; + passwordField_ = [accessory_view viewWithTag:kPasswordFieldTag]; + return accessory_view; +} + +- (void)focus { + [[alert_ window] makeFirstResponder:usernameField_]; +} + +- (void)alertDidEnd:(NSAlert*)alert + returnCode:(int)returnCode + contextInfo:(void*)contextInfo { + if (returnCode == NSRunStoppedResponse) + return; + + content::ShellLoginDialog* this_dialog = + reinterpret_cast<content::ShellLoginDialog*>(contextInfo); + if (returnCode == NSAlertFirstButtonReturn) { + this_dialog->UserAcceptedAuth( + base::SysNSStringToUTF16([usernameField_ stringValue]), + base::SysNSStringToUTF16([passwordField_ stringValue])); + } else { + this_dialog->UserCancelledAuth(); + } +} + +- (void)cancel { + [NSApp endSheet:[alert_ window]]; + alert_.reset(); +} + +@end + +namespace content { + +void ShellLoginDialog::PlatformCreateDialog(const string16& message) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + helper_ = [[ShellLoginDialogHelper alloc] init]; + + // Show the modal dialog. + NSAlert* alert = [helper_ alert]; + [alert setDelegate:helper_]; + [alert setInformativeText:base::SysUTF16ToNSString(message)]; + [alert setMessageText:@"Please log in."]; + [alert addButtonWithTitle:@"OK"]; + NSButton* other = [alert addButtonWithTitle:@"Cancel"]; + [other setKeyEquivalent:@"\e"]; + [alert + beginSheetModalForWindow:nil // nil here makes it app-modal + modalDelegate:helper_ + didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) + contextInfo:this]; + + [helper_ focus]; +} + +void ShellLoginDialog::PlatformCleanUp() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + [helper_ release]; + helper_ = nil; +} + +void ShellLoginDialog::PlatformRequestCancelled() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + [helper_ cancel]; +} + +} // namespace content diff --git a/content/shell/browser/shell_mac.mm b/content/shell/browser/shell_mac.mm new file mode 100644 index 0000000..ca6a0ff --- /dev/null +++ b/content/shell/browser/shell_mac.mm @@ -0,0 +1,334 @@ +// 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 "content/shell/browser/shell.h" + +#include <algorithm> + +#include "base/logging.h" +#import "base/mac/scoped_nsobject.h" +#include "base/strings/string_piece.h" +#include "base/strings/sys_string_conversions.h" +#include "content/public/browser/native_web_keyboard_event.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "content/shell/app/resource.h" +#import "ui/base/cocoa/underlay_opengl_hosting_window.h" +#include "url/gurl.h" + +#if !defined(MAC_OS_X_VERSION_10_7) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 + +enum { + NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7, + NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8 +}; + +#endif // MAC_OS_X_VERSION_10_7 + +// Receives notification that the window is closing so that it can start the +// tear-down process. Is responsible for deleting itself when done. +@interface ContentShellWindowDelegate : NSObject<NSWindowDelegate> { + @private + content::Shell* shell_; +} +- (id)initWithShell:(content::Shell*)shell; +@end + +@implementation ContentShellWindowDelegate + +- (id)initWithShell:(content::Shell*)shell { + if ((self = [super init])) { + shell_ = shell; + } + return self; +} + +// Called when the window is about to close. Perform the self-destruction +// sequence by getting rid of the shell and removing it and the window from +// the various global lists. By returning YES, we allow the window to be +// removed from the screen. +- (BOOL)windowShouldClose:(id)window { + [window autorelease]; + delete shell_; + [self release]; + + return YES; +} + +- (void)performAction:(id)sender { + shell_->ActionPerformed([sender tag]); +} + +- (void)takeURLStringValueFrom:(id)sender { + shell_->URLEntered(base::SysNSStringToUTF8([sender stringValue])); +} + +@end + +@interface CrShellWindow : UnderlayOpenGLHostingWindow { + @private + content::Shell* shell_; +} +- (void)setShell:(content::Shell*)shell; +- (void)showDevTools:(id)sender; +@end + +@implementation CrShellWindow + +- (void)setShell:(content::Shell*)shell { + shell_ = shell; +} + +- (void)showDevTools:(id)sender { + shell_->ShowDevTools(); +} + +@end + +namespace { + +NSString* kWindowTitle = @"Content Shell"; + +// Layout constants (in view coordinates) +const CGFloat kButtonWidth = 72; +const CGFloat kURLBarHeight = 24; + +// The minimum size of the window's content (in view coordinates) +const CGFloat kMinimumWindowWidth = 400; +const CGFloat kMinimumWindowHeight = 300; + +void MakeShellButton(NSRect* rect, + NSString* title, + NSView* parent, + int control, + NSView* target, + NSString* key, + NSUInteger modifier) { + base::scoped_nsobject<NSButton> button( + [[NSButton alloc] initWithFrame:*rect]); + [button setTitle:title]; + [button setBezelStyle:NSSmallSquareBezelStyle]; + [button setAutoresizingMask:(NSViewMaxXMargin | NSViewMinYMargin)]; + [button setTarget:target]; + [button setAction:@selector(performAction:)]; + [button setTag:control]; + [button setKeyEquivalent:key]; + [button setKeyEquivalentModifierMask:modifier]; + [parent addSubview:button]; + rect->origin.x += kButtonWidth; +} + +} // namespace + +namespace content { + +void Shell::PlatformInitialize(const gfx::Size& default_window_size) { +} + +void Shell::PlatformCleanUp() { +} + +void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) { + if (headless_) + return; + + int id; + switch (control) { + case BACK_BUTTON: + id = IDC_NAV_BACK; + break; + case FORWARD_BUTTON: + id = IDC_NAV_FORWARD; + break; + case STOP_BUTTON: + id = IDC_NAV_STOP; + break; + default: + NOTREACHED() << "Unknown UI control"; + return; + } + [[[window_ contentView] viewWithTag:id] setEnabled:is_enabled]; +} + +void Shell::PlatformSetAddressBarURL(const GURL& url) { + if (headless_) + return; + + NSString* url_string = base::SysUTF8ToNSString(url.spec()); + [url_edit_view_ setStringValue:url_string]; +} + +void Shell::PlatformSetIsLoading(bool loading) { +} + +void Shell::PlatformCreateWindow(int width, int height) { + if (headless_) { + content_width_ = width; + content_height_ = height; + return; + } + + NSRect initial_window_bounds = + NSMakeRect(0, 0, width, height + kURLBarHeight); + NSRect content_rect = initial_window_bounds; + NSUInteger style_mask = NSTitledWindowMask | + NSClosableWindowMask | + NSMiniaturizableWindowMask | + NSResizableWindowMask; + CrShellWindow* window = + [[CrShellWindow alloc] initWithContentRect:content_rect + styleMask:style_mask + backing:NSBackingStoreBuffered + defer:NO]; + window_ = window; + [window setShell:this]; + [window_ setTitle:kWindowTitle]; + NSView* content = [window_ contentView]; + + // If the window is allowed to get too small, it will wreck the view bindings. + NSSize min_size = NSMakeSize(kMinimumWindowWidth, kMinimumWindowHeight); + min_size = [content convertSize:min_size toView:nil]; + // Note that this takes window coordinates. + [window_ setContentMinSize:min_size]; + + // Set the shell window to participate in Lion Fullscreen mode. Set + // Setting this flag has no effect on Snow Leopard or earlier. + NSUInteger collectionBehavior = [window_ collectionBehavior]; + collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary; + [window_ setCollectionBehavior:collectionBehavior]; + + // Rely on the window delegate to clean us up rather than immediately + // releasing when the window gets closed. We use the delegate to do + // everything from the autorelease pool so the shell isn't on the stack + // during cleanup (ie, a window close from javascript). + [window_ setReleasedWhenClosed:NO]; + + // Create a window delegate to watch for when it's asked to go away. It will + // clean itself up so we don't need to hold a reference. + ContentShellWindowDelegate* delegate = + [[ContentShellWindowDelegate alloc] initWithShell:this]; + [window_ setDelegate:delegate]; + + NSRect button_frame = + NSMakeRect(0, NSMaxY(initial_window_bounds) - kURLBarHeight, + kButtonWidth, kURLBarHeight); + + MakeShellButton(&button_frame, @"Back", content, IDC_NAV_BACK, + (NSView*)delegate, @"[", NSCommandKeyMask); + MakeShellButton(&button_frame, @"Forward", content, IDC_NAV_FORWARD, + (NSView*)delegate, @"]", NSCommandKeyMask); + MakeShellButton(&button_frame, @"Reload", content, IDC_NAV_RELOAD, + (NSView*)delegate, @"r", NSCommandKeyMask); + MakeShellButton(&button_frame, @"Stop", content, IDC_NAV_STOP, + (NSView*)delegate, @".", NSCommandKeyMask); + + button_frame.size.width = + NSWidth(initial_window_bounds) - NSMinX(button_frame); + base::scoped_nsobject<NSTextField> url_edit_view( + [[NSTextField alloc] initWithFrame:button_frame]); + [content addSubview:url_edit_view]; + [url_edit_view setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)]; + [url_edit_view setTarget:delegate]; + [url_edit_view setAction:@selector(takeURLStringValueFrom:)]; + [[url_edit_view cell] setWraps:NO]; + [[url_edit_view cell] setScrollable:YES]; + url_edit_view_ = url_edit_view.get(); + + // show the window + [window_ makeKeyAndOrderFront:nil]; +} + +void Shell::PlatformSetContents() { + NSView* web_view = web_contents_->GetView()->GetNativeView(); + [web_view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; + + if (headless_) { + SizeTo(content_width_, content_height_); + return; + } + + NSView* content = [window_ contentView]; + [content addSubview:web_view]; + + NSRect frame = [content bounds]; + frame.size.height -= kURLBarHeight; + [web_view setFrame:frame]; + [web_view setNeedsDisplay:YES]; +} + +void Shell::SizeTo(int width, int height) { + if (!headless_) { + NOTREACHED(); + return; + } + NSView* web_view = web_contents_->GetView()->GetNativeView(); + NSRect frame = NSMakeRect(0, 0, width, height); + [web_view setFrame:frame]; +} + +void Shell::PlatformResizeSubViews() { + // Not needed; subviews are bound. +} + +void Shell::PlatformSetTitle(const string16& title) { + if (headless_) + return; + + NSString* title_string = base::SysUTF16ToNSString(title); + [window_ setTitle:title_string]; +} + +void Shell::Close() { + if (headless_) + delete this; + else + [window_ performClose:nil]; +} + +void Shell::ActionPerformed(int control) { + switch (control) { + case IDC_NAV_BACK: + GoBackOrForward(-1); + break; + case IDC_NAV_FORWARD: + GoBackOrForward(1); + break; + case IDC_NAV_RELOAD: + Reload(); + break; + case IDC_NAV_STOP: + Stop(); + break; + } +} + +void Shell::URLEntered(std::string url_string) { + if (!url_string.empty()) { + GURL url(url_string); + if (!url.has_scheme()) + url = GURL("http://" + url_string); + LoadURL(url); + } +} + +void Shell::HandleKeyboardEvent(WebContents* source, + const NativeWebKeyboardEvent& event) { + if (event.skip_in_browser) + return; + + // The event handling to get this strictly right is a tangle; cheat here a bit + // by just letting the menus have a chance at it. + if ([event.os_event type] == NSKeyDown) { + if (([event.os_event modifierFlags] & NSCommandKeyMask) && + [[event.os_event characters] isEqual:@"l"]) { + [window_ makeFirstResponder:url_edit_view_]; + return; + } + + [[NSApp mainMenu] performKeyEquivalent:event.os_event]; + } +} + +} // namespace content diff --git a/content/shell/browser/shell_message_filter.cc b/content/shell/browser/shell_message_filter.cc new file mode 100644 index 0000000..c7c4cca --- /dev/null +++ b/content/shell/browser/shell_message_filter.cc @@ -0,0 +1,105 @@ +// 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 "content/shell/browser/shell_message_filter.h" + +#include "base/file_util.h" +#include "base/threading/thread_restrictions.h" +#include "content/public/browser/child_process_security_policy.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/shell/browser/shell_content_browser_client.h" +#include "content/shell/browser/shell_network_delegate.h" +#include "content/shell/common/shell_messages.h" +#include "net/base/net_errors.h" +#include "net/cookies/cookie_monster.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_getter.h" +#include "webkit/browser/database/database_tracker.h" +#include "webkit/browser/fileapi/isolated_context.h" +#include "webkit/browser/quota/quota_manager.h" + +namespace content { + +ShellMessageFilter::ShellMessageFilter( + int render_process_id, + webkit_database::DatabaseTracker* database_tracker, + quota::QuotaManager* quota_manager, + net::URLRequestContextGetter* request_context_getter) + : render_process_id_(render_process_id), + database_tracker_(database_tracker), + quota_manager_(quota_manager), + request_context_getter_(request_context_getter) { +} + +ShellMessageFilter::~ShellMessageFilter() { +} + +void ShellMessageFilter::OverrideThreadForMessage(const IPC::Message& message, + BrowserThread::ID* thread) { + if (message.type() == ShellViewHostMsg_ClearAllDatabases::ID) + *thread = BrowserThread::FILE; +} + +bool ShellMessageFilter::OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX(ShellMessageFilter, message, *message_was_ok) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_ReadFileToString, OnReadFileToString) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_RegisterIsolatedFileSystem, + OnRegisterIsolatedFileSystem) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_ClearAllDatabases, OnClearAllDatabases) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_SetDatabaseQuota, OnSetDatabaseQuota) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_AcceptAllCookies, OnAcceptAllCookies) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_DeleteAllCookies, OnDeleteAllCookies) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; +} + +void ShellMessageFilter::OnReadFileToString(const base::FilePath& local_file, + std::string* contents) { + base::ThreadRestrictions::ScopedAllowIO allow_io; + file_util::ReadFileToString(local_file, contents); +} + +void ShellMessageFilter::OnRegisterIsolatedFileSystem( + const std::vector<base::FilePath>& absolute_filenames, + std::string* filesystem_id) { + fileapi::IsolatedContext::FileInfoSet files; + ChildProcessSecurityPolicy* policy = + ChildProcessSecurityPolicy::GetInstance(); + for (size_t i = 0; i < absolute_filenames.size(); ++i) { + files.AddPath(absolute_filenames[i], NULL); + if (!policy->CanReadFile(render_process_id_, absolute_filenames[i])) + policy->GrantReadFile(render_process_id_, absolute_filenames[i]); + } + *filesystem_id = + fileapi::IsolatedContext::GetInstance()->RegisterDraggedFileSystem(files); + policy->GrantReadFileSystem(render_process_id_, *filesystem_id); +} + +void ShellMessageFilter::OnClearAllDatabases() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + database_tracker_->DeleteDataModifiedSince( + base::Time(), net::CompletionCallback()); +} + +void ShellMessageFilter::OnSetDatabaseQuota(int quota) { + quota_manager_->SetTemporaryGlobalOverrideQuota( + quota * quota::QuotaManager::kPerHostTemporaryPortion, + quota::QuotaCallback()); +} + +void ShellMessageFilter::OnAcceptAllCookies(bool accept) { + ShellNetworkDelegate::SetAcceptAllCookies(accept); +} + +void ShellMessageFilter::OnDeleteAllCookies() { + request_context_getter_->GetURLRequestContext()->cookie_store() + ->GetCookieMonster() + ->DeleteAllAsync(net::CookieMonster::DeleteCallback()); +} + +} // namespace content diff --git a/content/shell/browser/shell_message_filter.h b/content/shell/browser/shell_message_filter.h new file mode 100644 index 0000000..135a915 --- /dev/null +++ b/content/shell/browser/shell_message_filter.h @@ -0,0 +1,66 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_MESSAGE_FILTER_H_ +#define CONTENT_SHELL_BROWSER_SHELL_MESSAGE_FILTER_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "content/public/browser/browser_message_filter.h" + +namespace net { +class URLRequestContextGetter; +} + +namespace quota { +class QuotaManager; +} + +namespace webkit_database { +class DatabaseTracker; +} + +namespace content { + +class ShellMessageFilter : public BrowserMessageFilter { + public: + ShellMessageFilter(int render_process_id, + webkit_database::DatabaseTracker* database_tracker, + quota::QuotaManager* quota_manager, + net::URLRequestContextGetter* request_context_getter); + + private: + virtual ~ShellMessageFilter(); + + // BrowserMessageFilter implementation. + virtual void OverrideThreadForMessage(const IPC::Message& message, + BrowserThread::ID* thread) OVERRIDE; + virtual bool OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) OVERRIDE; + + void OnReadFileToString(const base::FilePath& local_file, + std::string* contents); + void OnRegisterIsolatedFileSystem( + const std::vector<base::FilePath>& absolute_filenames, + std::string* filesystem_id); + void OnClearAllDatabases(); + void OnSetDatabaseQuota(int quota); + void OnAcceptAllCookies(bool accept); + void OnDeleteAllCookies(); + + int render_process_id_; + + webkit_database::DatabaseTracker* database_tracker_; + quota::QuotaManager* quota_manager_; + net::URLRequestContextGetter* request_context_getter_; + + DISALLOW_COPY_AND_ASSIGN(ShellMessageFilter); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_MESSAGE_FILTER_H_ diff --git a/content/shell/browser/shell_net_log.cc b/content/shell/browser/shell_net_log.cc new file mode 100644 index 0000000..97276d6 --- /dev/null +++ b/content/shell/browser/shell_net_log.cc @@ -0,0 +1,72 @@ +// 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 "content/shell/browser/shell_net_log.h" + +#include <stdio.h> + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/values.h" +#include "content/public/common/content_switches.h" +#include "net/base/net_log_logger.h" + +namespace content { + +namespace { + +base::DictionaryValue* GetShellConstants() { + base::DictionaryValue* constants_dict = net::NetLogLogger::GetConstants(); + + // Add a dictionary with client information + base::DictionaryValue* dict = new DictionaryValue(); + + dict->SetString("name", "content_shell"); + dict->SetString("command_line", + CommandLine::ForCurrentProcess()->GetCommandLineString()); + + constants_dict->Set("clientInfo", dict); + + return constants_dict; +} + +} // namespace + +ShellNetLog::ShellNetLog() { + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + + if (command_line->HasSwitch(switches::kLogNetLog)) { + base::FilePath log_path = + command_line->GetSwitchValuePath(switches::kLogNetLog); + // Much like logging.h, bypass threading restrictions by using fopen + // directly. Have to write on a thread that's shutdown to handle events on + // shutdown properly, and posting events to another thread as they occur + // would result in an unbounded buffer size, so not much can be gained by + // doing this on another thread. It's only used when debugging, so + // performance is not a big concern. + FILE* file = NULL; +#if defined(OS_WIN) + file = _wfopen(log_path.value().c_str(), L"w"); +#elif defined(OS_POSIX) + file = fopen(log_path.value().c_str(), "w"); +#endif + + if (file == NULL) { + LOG(ERROR) << "Could not open file " << log_path.value() + << " for net logging"; + } else { + scoped_ptr<base::Value> constants(GetShellConstants()); + net_log_logger_.reset(new net::NetLogLogger(file, *constants)); + net_log_logger_->StartObserving(this); + } + } +} + +ShellNetLog::~ShellNetLog() { + // Remove the observer we own before we're destroyed. + if (net_log_logger_) + RemoveThreadSafeObserver(net_log_logger_.get()); +} + +} // namespace content diff --git a/content/shell/browser/shell_net_log.h b/content/shell/browser/shell_net_log.h new file mode 100644 index 0000000..9846917 --- /dev/null +++ b/content/shell/browser/shell_net_log.h @@ -0,0 +1,28 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_NET_LOG_H_ +#define CONTENT_SHELL_BROWSER_SHELL_NET_LOG_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "net/base/net_log_logger.h" + +namespace content { + +class ShellNetLog : public net::NetLog { + public: + ShellNetLog(); + virtual ~ShellNetLog(); + + private: + scoped_ptr<net::NetLogLogger> net_log_logger_; + + DISALLOW_COPY_AND_ASSIGN(ShellNetLog); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_NET_LOG_H_ diff --git a/content/shell/browser/shell_network_delegate.cc b/content/shell/browser/shell_network_delegate.cc new file mode 100644 index 0000000..560426f --- /dev/null +++ b/content/shell/browser/shell_network_delegate.cc @@ -0,0 +1,127 @@ +// 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 "content/shell/browser/shell_network_delegate.h" + +#include "net/base/net_errors.h" +#include "net/base/static_cookie_policy.h" +#include "net/url_request/url_request.h" + +namespace content { + +namespace { +bool g_accept_all_cookies = true; +} + +ShellNetworkDelegate::ShellNetworkDelegate() { +} + +ShellNetworkDelegate::~ShellNetworkDelegate() { +} + +void ShellNetworkDelegate::SetAcceptAllCookies(bool accept) { + g_accept_all_cookies = accept; +} + +int ShellNetworkDelegate::OnBeforeURLRequest( + net::URLRequest* request, + const net::CompletionCallback& callback, + GURL* new_url) { + return net::OK; +} + +int ShellNetworkDelegate::OnBeforeSendHeaders( + net::URLRequest* request, + const net::CompletionCallback& callback, + net::HttpRequestHeaders* headers) { + return net::OK; +} + +void ShellNetworkDelegate::OnSendHeaders( + net::URLRequest* request, + const net::HttpRequestHeaders& headers) { +} + +int ShellNetworkDelegate::OnHeadersReceived( + net::URLRequest* request, + const net::CompletionCallback& callback, + const net::HttpResponseHeaders* original_response_headers, + scoped_refptr<net::HttpResponseHeaders>* override_response_headers) { + return net::OK; +} + +void ShellNetworkDelegate::OnBeforeRedirect(net::URLRequest* request, + const GURL& new_location) { +} + +void ShellNetworkDelegate::OnResponseStarted(net::URLRequest* request) { +} + +void ShellNetworkDelegate::OnRawBytesRead(const net::URLRequest& request, + int bytes_read) { +} + +void ShellNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { +} + +void ShellNetworkDelegate::OnURLRequestDestroyed(net::URLRequest* request) { +} + +void ShellNetworkDelegate::OnPACScriptError(int line_number, + const string16& error) { +} + +ShellNetworkDelegate::AuthRequiredResponse ShellNetworkDelegate::OnAuthRequired( + net::URLRequest* request, + const net::AuthChallengeInfo& auth_info, + const AuthCallback& callback, + net::AuthCredentials* credentials) { + return AUTH_REQUIRED_RESPONSE_NO_ACTION; +} + +bool ShellNetworkDelegate::OnCanGetCookies(const net::URLRequest& request, + const net::CookieList& cookie_list) { + net::StaticCookiePolicy::Type policy_type = g_accept_all_cookies ? + net::StaticCookiePolicy::ALLOW_ALL_COOKIES : + net::StaticCookiePolicy::BLOCK_SETTING_THIRD_PARTY_COOKIES; + net::StaticCookiePolicy policy(policy_type); + int rv = policy.CanGetCookies( + request.url(), request.first_party_for_cookies()); + return rv == net::OK; +} + +bool ShellNetworkDelegate::OnCanSetCookie(const net::URLRequest& request, + const std::string& cookie_line, + net::CookieOptions* options) { + net::StaticCookiePolicy::Type policy_type = g_accept_all_cookies ? + net::StaticCookiePolicy::ALLOW_ALL_COOKIES : + net::StaticCookiePolicy::BLOCK_SETTING_THIRD_PARTY_COOKIES; + net::StaticCookiePolicy policy(policy_type); + int rv = policy.CanSetCookie( + request.url(), request.first_party_for_cookies()); + return rv == net::OK; +} + +bool ShellNetworkDelegate::OnCanAccessFile(const net::URLRequest& request, + const base::FilePath& path) const { + return true; +} + +bool ShellNetworkDelegate::OnCanThrottleRequest( + const net::URLRequest& request) const { + return false; +} + +int ShellNetworkDelegate::OnBeforeSocketStreamConnect( + net::SocketStream* socket, + const net::CompletionCallback& callback) { + return net::OK; +} + +void ShellNetworkDelegate::OnRequestWaitStateChange( + const net::URLRequest& request, + RequestWaitState waiting) { +} + +} // namespace content diff --git a/content/shell/browser/shell_network_delegate.h b/content/shell/browser/shell_network_delegate.h new file mode 100644 index 0000000..a3ce0a2 --- /dev/null +++ b/content/shell/browser/shell_network_delegate.h @@ -0,0 +1,71 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_NETWORK_DELEGATE_H_ +#define CONTENT_SHELL_BROWSER_SHELL_NETWORK_DELEGATE_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "net/base/network_delegate.h" + +namespace content { + +class ShellNetworkDelegate : public net::NetworkDelegate { + public: + ShellNetworkDelegate(); + virtual ~ShellNetworkDelegate(); + + static void SetAcceptAllCookies(bool accept); + + private: + // net::NetworkDelegate implementation. + virtual int OnBeforeURLRequest(net::URLRequest* request, + const net::CompletionCallback& callback, + GURL* new_url) OVERRIDE; + virtual int OnBeforeSendHeaders(net::URLRequest* request, + const net::CompletionCallback& callback, + net::HttpRequestHeaders* headers) OVERRIDE; + virtual void OnSendHeaders(net::URLRequest* request, + const net::HttpRequestHeaders& headers) OVERRIDE; + virtual int OnHeadersReceived( + net::URLRequest* request, + const net::CompletionCallback& callback, + const net::HttpResponseHeaders* original_response_headers, + scoped_refptr<net::HttpResponseHeaders>* + override_response_headers) OVERRIDE; + virtual void OnBeforeRedirect(net::URLRequest* request, + const GURL& new_location) OVERRIDE; + virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE; + virtual void OnRawBytesRead(const net::URLRequest& request, + int bytes_read) OVERRIDE; + virtual void OnCompleted(net::URLRequest* request, bool started) OVERRIDE; + virtual void OnURLRequestDestroyed(net::URLRequest* request) OVERRIDE; + virtual void OnPACScriptError(int line_number, + const string16& error) OVERRIDE; + virtual AuthRequiredResponse OnAuthRequired( + net::URLRequest* request, + const net::AuthChallengeInfo& auth_info, + const AuthCallback& callback, + net::AuthCredentials* credentials) OVERRIDE; + virtual bool OnCanGetCookies(const net::URLRequest& request, + const net::CookieList& cookie_list) OVERRIDE; + virtual bool OnCanSetCookie(const net::URLRequest& request, + const std::string& cookie_line, + net::CookieOptions* options) OVERRIDE; + virtual bool OnCanAccessFile(const net::URLRequest& request, + const base::FilePath& path) const OVERRIDE; + virtual bool OnCanThrottleRequest( + const net::URLRequest& request) const OVERRIDE; + virtual int OnBeforeSocketStreamConnect( + net::SocketStream* stream, + const net::CompletionCallback& callback) OVERRIDE; + virtual void OnRequestWaitStateChange(const net::URLRequest& request, + RequestWaitState state) OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(ShellNetworkDelegate); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_NETWORK_DELEGATE_H_ diff --git a/content/shell/browser/shell_plugin_service_filter.cc b/content/shell/browser/shell_plugin_service_filter.cc new file mode 100644 index 0000000..d41b9bd --- /dev/null +++ b/content/shell/browser/shell_plugin_service_filter.cc @@ -0,0 +1,31 @@ +// 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 "content/shell/browser/shell_plugin_service_filter.h" + +#include "base/strings/utf_string_conversions.h" +#include "content/public/common/webplugininfo.h" + +namespace content { + +ShellPluginServiceFilter::ShellPluginServiceFilter() {} + +ShellPluginServiceFilter::~ShellPluginServiceFilter() {} + +bool ShellPluginServiceFilter::IsPluginAvailable( + int render_process_id, + int render_view_id, + const void* context, + const GURL& url, + const GURL& policy_url, + WebPluginInfo* plugin) { + return plugin->name == ASCIIToUTF16("WebKit Test PlugIn"); +} + +bool ShellPluginServiceFilter::CanLoadPlugin(int render_process_id, + const base::FilePath& path) { + return true; +} + +} // namespace content diff --git a/content/shell/browser/shell_plugin_service_filter.h b/content/shell/browser/shell_plugin_service_filter.h new file mode 100644 index 0000000..3e9cb5d --- /dev/null +++ b/content/shell/browser/shell_plugin_service_filter.h @@ -0,0 +1,37 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_PLUGIN_SERVICE_FILTER_H_ +#define CONTENT_SHELL_BROWSER_SHELL_PLUGIN_SERVICE_FILTER_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "content/public/browser/plugin_service_filter.h" + +namespace content { + +class ShellPluginServiceFilter : public PluginServiceFilter { + public: + ShellPluginServiceFilter(); + virtual ~ShellPluginServiceFilter(); + + // PluginServiceFilter implementation. + virtual bool IsPluginAvailable(int render_process_id, + int render_view_id, + const void* context, + const GURL& url, + const GURL& policy_url, + WebPluginInfo* plugin) OVERRIDE; + + virtual bool CanLoadPlugin(int render_process_id, + const base::FilePath& path) OVERRIDE; + + private: + + DISALLOW_COPY_AND_ASSIGN(ShellPluginServiceFilter); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_PLUGIN_SERVICE_FILTER_H_ diff --git a/content/shell/browser/shell_quota_permission_context.cc b/content/shell/browser/shell_quota_permission_context.cc new file mode 100644 index 0000000..064322f --- /dev/null +++ b/content/shell/browser/shell_quota_permission_context.cc @@ -0,0 +1,32 @@ +// 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 "content/shell/browser/shell_quota_permission_context.h" + +#include "webkit/common/quota/quota_types.h" + +namespace content { + +ShellQuotaPermissionContext::ShellQuotaPermissionContext() {} + +void ShellQuotaPermissionContext::RequestQuotaPermission( + const GURL& origin_url, + quota::StorageType type, + int64 requested_quota, + int render_process_id, + int render_view_id, + const PermissionCallback& callback) { + if (type != quota::kStorageTypePersistent) { + // For now we only support requesting quota with this interface + // for Persistent storage type. + callback.Run(QUOTA_PERMISSION_RESPONSE_DISALLOW); + return; + } + + callback.Run(QUOTA_PERMISSION_RESPONSE_ALLOW); +} + +ShellQuotaPermissionContext::~ShellQuotaPermissionContext() {} + +} // namespace content diff --git a/content/shell/browser/shell_quota_permission_context.h b/content/shell/browser/shell_quota_permission_context.h new file mode 100644 index 0000000..381338d --- /dev/null +++ b/content/shell/browser/shell_quota_permission_context.h @@ -0,0 +1,34 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_QUOTA_PERMISSION_CONTEXT_H_ +#define CONTENT_SHELL_BROWSER_SHELL_QUOTA_PERMISSION_CONTEXT_H_ + +#include "base/compiler_specific.h" +#include "content/public/browser/quota_permission_context.h" + +namespace content { + +class ShellQuotaPermissionContext : public QuotaPermissionContext { + public: + ShellQuotaPermissionContext(); + + // The callback will be dispatched on the IO thread. + virtual void RequestQuotaPermission( + const GURL& origin_url, + quota::StorageType type, + int64 new_quota, + int render_process_id, + int render_view_id, + const PermissionCallback& callback) OVERRIDE; + + private: + virtual ~ShellQuotaPermissionContext(); + + DISALLOW_COPY_AND_ASSIGN(ShellQuotaPermissionContext); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_QUOTA_PERMISSION_CONTEXT_H_ diff --git a/content/shell/browser/shell_resource_dispatcher_host_delegate.cc b/content/shell/browser/shell_resource_dispatcher_host_delegate.cc new file mode 100644 index 0000000..d0994b1 --- /dev/null +++ b/content/shell/browser/shell_resource_dispatcher_host_delegate.cc @@ -0,0 +1,44 @@ +// 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 "content/shell/browser/shell_resource_dispatcher_host_delegate.h" + +#include "base/command_line.h" +#include "content/shell/browser/shell_login_dialog.h" +#include "content/shell/common/shell_switches.h" + +namespace content { + +ShellResourceDispatcherHostDelegate::ShellResourceDispatcherHostDelegate() { +} + +ShellResourceDispatcherHostDelegate::~ShellResourceDispatcherHostDelegate() { +} + +bool ShellResourceDispatcherHostDelegate::AcceptAuthRequest( + net::URLRequest* request, + net::AuthChallengeInfo* auth_info) { + bool accept_auth_request = + !CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree); + return accept_auth_request; +} + +ResourceDispatcherHostLoginDelegate* +ShellResourceDispatcherHostDelegate::CreateLoginDelegate( + net::AuthChallengeInfo* auth_info, net::URLRequest* request) { + if (!login_request_callback_.is_null()) { + login_request_callback_.Run(); + login_request_callback_.Reset(); + return NULL; + } + +#if !defined(OS_MACOSX) && !defined(TOOLKIT_GTK) +// TODO: implement ShellLoginDialog for other platforms, drop this #if + return NULL; +#else + return new ShellLoginDialog(auth_info, request); +#endif +} + +} // namespace content diff --git a/content/shell/browser/shell_resource_dispatcher_host_delegate.h b/content/shell/browser/shell_resource_dispatcher_host_delegate.h new file mode 100644 index 0000000..90e6d4c --- /dev/null +++ b/content/shell/browser/shell_resource_dispatcher_host_delegate.h @@ -0,0 +1,40 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ +#define CONTENT_SHELL_BROWSER_SHELL_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "content/public/browser/resource_dispatcher_host_delegate.h" + +namespace content { + +class ShellResourceDispatcherHostDelegate + : public ResourceDispatcherHostDelegate { + public: + ShellResourceDispatcherHostDelegate(); + virtual ~ShellResourceDispatcherHostDelegate(); + + // ResourceDispatcherHostDelegate implementation. + virtual bool AcceptAuthRequest(net::URLRequest* request, + net::AuthChallengeInfo* auth_info) OVERRIDE; + virtual ResourceDispatcherHostLoginDelegate* CreateLoginDelegate( + net::AuthChallengeInfo* auth_info, net::URLRequest* request) OVERRIDE; + + // Used for content_browsertests. + void set_login_request_callback( + base::Callback<void()> login_request_callback) { + login_request_callback_ = login_request_callback; + } + + private: + base::Callback<void()> login_request_callback_; + + DISALLOW_COPY_AND_ASSIGN(ShellResourceDispatcherHostDelegate); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ diff --git a/content/shell/browser/shell_url_request_context_getter.cc b/content/shell/browser/shell_url_request_context_getter.cc new file mode 100644 index 0000000..dfdb852 --- /dev/null +++ b/content/shell/browser/shell_url_request_context_getter.cc @@ -0,0 +1,231 @@ +// 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 "content/shell/browser/shell_url_request_context_getter.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/threading/worker_pool.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/url_constants.h" +#include "content/shell/browser/shell_network_delegate.h" +#include "content/shell/common/shell_switches.h" +#include "net/base/cache_type.h" +#include "net/cert/cert_verifier.h" +#include "net/cookies/cookie_monster.h" +#include "net/dns/host_resolver.h" +#include "net/dns/mapped_host_resolver.h" +#include "net/http/http_auth_handler_factory.h" +#include "net/http/http_cache.h" +#include "net/http/http_network_session.h" +#include "net/http/http_server_properties_impl.h" +#include "net/http/transport_security_state.h" +#include "net/proxy/proxy_service.h" +#include "net/ssl/default_server_bound_cert_store.h" +#include "net/ssl/server_bound_cert_service.h" +#include "net/ssl/ssl_config_service_defaults.h" +#include "net/url_request/data_protocol_handler.h" +#include "net/url_request/file_protocol_handler.h" +#include "net/url_request/protocol_intercept_job_factory.h" +#include "net/url_request/static_http_user_agent_settings.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_storage.h" +#include "net/url_request/url_request_job_factory_impl.h" + +namespace content { + +namespace { + +void InstallProtocolHandlers(net::URLRequestJobFactoryImpl* job_factory, + ProtocolHandlerMap* protocol_handlers) { + for (ProtocolHandlerMap::iterator it = + protocol_handlers->begin(); + it != protocol_handlers->end(); + ++it) { + bool set_protocol = job_factory->SetProtocolHandler( + it->first, it->second.release()); + DCHECK(set_protocol); + } + protocol_handlers->clear(); +} + +} // namespace + +ShellURLRequestContextGetter::ShellURLRequestContextGetter( + bool ignore_certificate_errors, + const base::FilePath& base_path, + base::MessageLoop* io_loop, + base::MessageLoop* file_loop, + ProtocolHandlerMap* protocol_handlers, + net::NetLog* net_log) + : ignore_certificate_errors_(ignore_certificate_errors), + base_path_(base_path), + io_loop_(io_loop), + file_loop_(file_loop), + net_log_(net_log) { + // Must first be created on the UI thread. + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + std::swap(protocol_handlers_, *protocol_handlers); + + // We must create the proxy config service on the UI loop on Linux because it + // must synchronously run on the glib message loop. This will be passed to + // the URLRequestContextStorage on the IO thread in GetURLRequestContext(). + if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) { + proxy_config_service_.reset( + net::ProxyService::CreateSystemProxyConfigService( + io_loop_->message_loop_proxy().get(), file_loop_)); + } +} + +ShellURLRequestContextGetter::~ShellURLRequestContextGetter() { +} + +net::URLRequestContext* ShellURLRequestContextGetter::GetURLRequestContext() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!url_request_context_) { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + + url_request_context_.reset(new net::URLRequestContext()); + url_request_context_->set_net_log(net_log_); + network_delegate_.reset(new ShellNetworkDelegate); + if (command_line.HasSwitch(switches::kDumpRenderTree)) + ShellNetworkDelegate::SetAcceptAllCookies(false); + url_request_context_->set_network_delegate(network_delegate_.get()); + storage_.reset( + new net::URLRequestContextStorage(url_request_context_.get())); + storage_->set_cookie_store(new net::CookieMonster(NULL, NULL)); + storage_->set_server_bound_cert_service(new net::ServerBoundCertService( + new net::DefaultServerBoundCertStore(NULL), + base::WorkerPool::GetTaskRunner(true))); + storage_->set_http_user_agent_settings( + new net::StaticHttpUserAgentSettings("en-us,en", EmptyString())); + + scoped_ptr<net::HostResolver> host_resolver( + net::HostResolver::CreateDefaultResolver( + url_request_context_->net_log())); + + storage_->set_cert_verifier(net::CertVerifier::CreateDefault()); + storage_->set_transport_security_state(new net::TransportSecurityState); + if (command_line.HasSwitch(switches::kDumpRenderTree)) { + storage_->set_proxy_service(net::ProxyService::CreateDirect()); + } else { + // TODO(jam): use v8 if possible, look at chrome code. + storage_->set_proxy_service( + net::ProxyService::CreateUsingSystemProxyResolver( + proxy_config_service_.release(), + 0, + url_request_context_->net_log())); + } + storage_->set_ssl_config_service(new net::SSLConfigServiceDefaults); + storage_->set_http_auth_handler_factory( + net::HttpAuthHandlerFactory::CreateDefault(host_resolver.get())); + storage_->set_http_server_properties( + scoped_ptr<net::HttpServerProperties>( + new net::HttpServerPropertiesImpl())); + + base::FilePath cache_path = base_path_.Append(FILE_PATH_LITERAL("Cache")); + net::HttpCache::DefaultBackend* main_backend = + new net::HttpCache::DefaultBackend( + net::DISK_CACHE, +#if defined(OS_ANDROID) + // TODO(rdsmith): Remove when default backend for Android is + // changed to simple cache. + net::CACHE_BACKEND_SIMPLE, +#else + net::CACHE_BACKEND_DEFAULT, +#endif + cache_path, + 0, + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE) + .get()); + + net::HttpNetworkSession::Params network_session_params; + network_session_params.cert_verifier = + url_request_context_->cert_verifier(); + network_session_params.transport_security_state = + url_request_context_->transport_security_state(); + network_session_params.server_bound_cert_service = + url_request_context_->server_bound_cert_service(); + network_session_params.proxy_service = + url_request_context_->proxy_service(); + network_session_params.ssl_config_service = + url_request_context_->ssl_config_service(); + network_session_params.http_auth_handler_factory = + url_request_context_->http_auth_handler_factory(); + network_session_params.network_delegate = + network_delegate_.get(); + network_session_params.http_server_properties = + url_request_context_->http_server_properties(); + network_session_params.net_log = + url_request_context_->net_log(); + network_session_params.ignore_certificate_errors = + ignore_certificate_errors_; + if (command_line.HasSwitch(switches::kTestingFixedHttpPort)) { + int value; + base::StringToInt(command_line.GetSwitchValueASCII( + switches::kTestingFixedHttpPort), &value); + network_session_params.testing_fixed_http_port = value; + } + if (command_line.HasSwitch(switches::kTestingFixedHttpsPort)) { + int value; + base::StringToInt(command_line.GetSwitchValueASCII( + switches::kTestingFixedHttpsPort), &value); + network_session_params.testing_fixed_https_port = value; + } + if (command_line.HasSwitch(switches::kHostResolverRules)) { + scoped_ptr<net::MappedHostResolver> mapped_host_resolver( + new net::MappedHostResolver(host_resolver.Pass())); + mapped_host_resolver->SetRulesFromString( + command_line.GetSwitchValueASCII(switches::kHostResolverRules)); + host_resolver = mapped_host_resolver.Pass(); + } + + // Give |storage_| ownership at the end in case it's |mapped_host_resolver|. + storage_->set_host_resolver(host_resolver.Pass()); + network_session_params.host_resolver = + url_request_context_->host_resolver(); + + net::HttpCache* main_cache = new net::HttpCache( + network_session_params, main_backend); + storage_->set_http_transaction_factory(main_cache); + + scoped_ptr<net::URLRequestJobFactoryImpl> job_factory( + new net::URLRequestJobFactoryImpl()); + // Keep ProtocolHandlers added in sync with + // ShellContentBrowserClient::IsHandledURL(). + InstallProtocolHandlers(job_factory.get(), &protocol_handlers_); + bool set_protocol = job_factory->SetProtocolHandler( + chrome::kDataScheme, + new net::DataProtocolHandler); + DCHECK(set_protocol); + set_protocol = job_factory->SetProtocolHandler( + chrome::kFileScheme, + new net::FileProtocolHandler( + content::BrowserThread::GetBlockingPool()-> + GetTaskRunnerWithShutdownBehavior( + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN))); + DCHECK(set_protocol); + storage_->set_job_factory(job_factory.release()); + } + + return url_request_context_.get(); +} + +scoped_refptr<base::SingleThreadTaskRunner> + ShellURLRequestContextGetter::GetNetworkTaskRunner() const { + return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); +} + +net::HostResolver* ShellURLRequestContextGetter::host_resolver() { + return url_request_context_->host_resolver(); +} + +} // namespace content diff --git a/content/shell/browser/shell_url_request_context_getter.h b/content/shell/browser/shell_url_request_context_getter.h new file mode 100644 index 0000000..b18c0a9 --- /dev/null +++ b/content/shell/browser/shell_url_request_context_getter.h @@ -0,0 +1,69 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_URL_REQUEST_CONTEXT_GETTER_H_ +#define CONTENT_SHELL_BROWSER_SHELL_URL_REQUEST_CONTEXT_GETTER_H_ + +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/content_browser_client.h" +#include "net/url_request/url_request_context_getter.h" +#include "net/url_request/url_request_job_factory.h" + +namespace base { +class MessageLoop; +} + +namespace net { +class HostResolver; +class MappedHostResolver; +class NetworkDelegate; +class NetLog; +class ProxyConfigService; +class URLRequestContextStorage; +} + +namespace content { + +class ShellURLRequestContextGetter : public net::URLRequestContextGetter { + public: + ShellURLRequestContextGetter( + bool ignore_certificate_errors, + const base::FilePath& base_path, + base::MessageLoop* io_loop, + base::MessageLoop* file_loop, + ProtocolHandlerMap* protocol_handlers, + net::NetLog* net_log); + + // net::URLRequestContextGetter implementation. + virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE; + virtual scoped_refptr<base::SingleThreadTaskRunner> + GetNetworkTaskRunner() const OVERRIDE; + + net::HostResolver* host_resolver(); + + protected: + virtual ~ShellURLRequestContextGetter(); + + private: + bool ignore_certificate_errors_; + base::FilePath base_path_; + base::MessageLoop* io_loop_; + base::MessageLoop* file_loop_; + net::NetLog* net_log_; + + scoped_ptr<net::ProxyConfigService> proxy_config_service_; + scoped_ptr<net::NetworkDelegate> network_delegate_; + scoped_ptr<net::URLRequestContextStorage> storage_; + scoped_ptr<net::URLRequestContext> url_request_context_; + ProtocolHandlerMap protocol_handlers_; + + DISALLOW_COPY_AND_ASSIGN(ShellURLRequestContextGetter); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_URL_REQUEST_CONTEXT_GETTER_H_ diff --git a/content/shell/browser/shell_web_contents_view_delegate.h b/content/shell/browser/shell_web_contents_view_delegate.h new file mode 100644 index 0000000..86d97b4 --- /dev/null +++ b/content/shell/browser/shell_web_contents_view_delegate.h @@ -0,0 +1,83 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_WEB_CONTENTS_VIEW_DELEGATE_H_ +#define CONTENT_SHELL_BROWSER_SHELL_WEB_CONTENTS_VIEW_DELEGATE_H_ + +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view_delegate.h" +#include "content/public/common/context_menu_params.h" + +#if defined(TOOLKIT_GTK) +#include "ui/base/gtk/gtk_signal.h" +#include "ui/base/gtk/owned_widget_gtk.h" +#endif + +namespace content { + +class ShellWebContentsViewDelegate : public WebContentsViewDelegate { + public: + explicit ShellWebContentsViewDelegate(WebContents* web_contents); + virtual ~ShellWebContentsViewDelegate(); + + // Overridden from WebContentsViewDelegate: + virtual void ShowContextMenu(const ContextMenuParams& params) OVERRIDE; + virtual WebDragDestDelegate* GetDragDestDelegate() OVERRIDE; + +#if defined(TOOLKIT_GTK) + virtual void Initialize(GtkWidget* expanded_container, + ui::FocusStoreGtk* focus_store) OVERRIDE; + virtual gfx::NativeView GetNativeView() const OVERRIDE; + virtual void Focus() OVERRIDE; + virtual gboolean OnNativeViewFocusEvent(GtkWidget* widget, + GtkDirectionType type, + gboolean* return_value) OVERRIDE; +#elif defined(OS_MACOSX) + virtual NSObject<RenderWidgetHostViewMacDelegate>* + CreateRenderWidgetHostViewDelegate( + RenderWidgetHost* render_widget_host) OVERRIDE; + void ActionPerformed(int id); +#elif defined(OS_WIN) + virtual void StoreFocus() OVERRIDE; + virtual void RestoreFocus() OVERRIDE; + virtual bool Focus() OVERRIDE; + virtual void TakeFocus(bool reverse) OVERRIDE; + virtual void SizeChanged(const gfx::Size& size) OVERRIDE; + void MenuItemSelected(int selection); +#endif + + private: + WebContents* web_contents_; + ContextMenuParams params_; + +#if defined(TOOLKIT_GTK) + ui::OwnedWidgetGtk floating_; + GtkWidget* expanded_container_; + + CHROMEGTK_CALLBACK_0(ShellWebContentsViewDelegate, void, + OnBackMenuActivated); + CHROMEGTK_CALLBACK_0(ShellWebContentsViewDelegate, void, + OnForwardMenuActivated); + CHROMEGTK_CALLBACK_0(ShellWebContentsViewDelegate, void, + OnReloadMenuActivated); + CHROMEGTK_CALLBACK_0(ShellWebContentsViewDelegate, void, + OnOpenURLMenuActivated); + CHROMEGTK_CALLBACK_0(ShellWebContentsViewDelegate, void, + OnCutMenuActivated); + CHROMEGTK_CALLBACK_0(ShellWebContentsViewDelegate, void, + OnCopyMenuActivated); + CHROMEGTK_CALLBACK_0(ShellWebContentsViewDelegate, void, + OnPasteMenuActivated); + CHROMEGTK_CALLBACK_0(ShellWebContentsViewDelegate, void, + OnDeleteMenuActivated); + CHROMEGTK_CALLBACK_0(ShellWebContentsViewDelegate, void, + OnInspectMenuActivated); +#endif + + DISALLOW_COPY_AND_ASSIGN(ShellWebContentsViewDelegate); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_WEB_CONTENTS_VIEW_DELEGATE_H_ diff --git a/content/shell/browser/shell_web_contents_view_delegate_android.cc b/content/shell/browser/shell_web_contents_view_delegate_android.cc new file mode 100644 index 0000000..3da5475 --- /dev/null +++ b/content/shell/browser/shell_web_contents_view_delegate_android.cc @@ -0,0 +1,46 @@ +// 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 "content/shell/browser/shell_web_contents_view_delegate.h" + +#include "base/command_line.h" +#include "content/public/browser/android/content_view_core.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "content/public/common/context_menu_params.h" +#include "content/shell/browser/shell_web_contents_view_delegate_creator.h" + +namespace content { + +WebContentsViewDelegate* CreateShellWebContentsViewDelegate( + WebContents* web_contents) { + return new ShellWebContentsViewDelegate(web_contents); +} + + +ShellWebContentsViewDelegate::ShellWebContentsViewDelegate( + WebContents* web_contents) + : web_contents_(web_contents) { +} + +ShellWebContentsViewDelegate::~ShellWebContentsViewDelegate() { +} + +void ShellWebContentsViewDelegate::ShowContextMenu( + const ContextMenuParams& params) { + if (params.is_editable && params.selection_text.empty()) { + content::ContentViewCore* content_view_core = + ContentViewCore::FromWebContents(web_contents_); + if (content_view_core) { + content_view_core->ShowPastePopup(params.selection_start.x(), + params.selection_start.y()); + } + } +} + +WebDragDestDelegate* ShellWebContentsViewDelegate::GetDragDestDelegate() { + return NULL; +} + +} // namespace content diff --git a/content/shell/browser/shell_web_contents_view_delegate_creator.h b/content/shell/browser/shell_web_contents_view_delegate_creator.h new file mode 100644 index 0000000..819ce1e --- /dev/null +++ b/content/shell/browser/shell_web_contents_view_delegate_creator.h @@ -0,0 +1,18 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_WEB_CONTENTS_VIEW_DELEGATE_CREATOR_H_ +#define CONTENT_SHELL_BROWSER_SHELL_WEB_CONTENTS_VIEW_DELEGATE_CREATOR_H_ + +namespace content { + +class WebContents; +class WebContentsViewDelegate; + +WebContentsViewDelegate* CreateShellWebContentsViewDelegate( + WebContents* web_contents); + +} // namespace content + +#endif //CONTENT_SHELL_BROWSER_SHELL_WEB_CONTENTS_VIEW_DELEGATE_CREATOR_H_ diff --git a/content/shell/browser/shell_web_contents_view_delegate_gtk.cc b/content/shell/browser/shell_web_contents_view_delegate_gtk.cc new file mode 100644 index 0000000..4c98257 --- /dev/null +++ b/content/shell/browser/shell_web_contents_view_delegate_gtk.cc @@ -0,0 +1,241 @@ +// 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 "content/shell/browser/shell_web_contents_view_delegate.h" + +#include "base/command_line.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "content/public/common/context_menu_params.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/shell/browser/shell_browser_main_parts.h" +#include "content/shell/browser/shell_content_browser_client.h" +#include "content/shell/browser/shell_devtools_frontend.h" +#include "content/shell/browser/shell_web_contents_view_delegate_creator.h" +#include "content/shell/common/shell_switches.h" +#include "third_party/WebKit/public/web/WebContextMenuData.h" +#include "ui/base/gtk/focus_store_gtk.h" +#include "ui/base/gtk/gtk_floating_container.h" + +using WebKit::WebContextMenuData; + +namespace content { + +WebContentsViewDelegate* CreateShellWebContentsViewDelegate( + WebContents* web_contents) { + return new ShellWebContentsViewDelegate(web_contents); +} + +ShellWebContentsViewDelegate::ShellWebContentsViewDelegate( + WebContents* web_contents) + : web_contents_(web_contents), + floating_(gtk_floating_container_new()) { +} + +ShellWebContentsViewDelegate::~ShellWebContentsViewDelegate() { + floating_.Destroy(); +} + +void ShellWebContentsViewDelegate::ShowContextMenu( + const ContextMenuParams& params) { + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) + return; + + GtkWidget* menu = gtk_menu_new(); + + params_ = params; + bool has_link = !params_.unfiltered_link_url.is_empty(); + bool has_selection = !params_.selection_text.empty(); + + if (params_.media_type == WebContextMenuData::MediaTypeNone && + !has_link && + !has_selection && + !params_.is_editable) { + GtkWidget* back_menu = gtk_menu_item_new_with_label("Back"); + gtk_menu_append(GTK_MENU(menu), back_menu); + g_signal_connect(back_menu, + "activate", + G_CALLBACK(OnBackMenuActivatedThunk), + this); + gtk_widget_set_sensitive(back_menu, + web_contents_->GetController().CanGoBack()); + + GtkWidget* forward_menu = gtk_menu_item_new_with_label("Forward"); + gtk_menu_append(GTK_MENU(menu), forward_menu); + g_signal_connect(forward_menu, + "activate", + G_CALLBACK(OnForwardMenuActivatedThunk), + this); + gtk_widget_set_sensitive(forward_menu, + web_contents_->GetController().CanGoForward()); + + GtkWidget* reload_menu = gtk_menu_item_new_with_label("Reload"); + gtk_menu_append(GTK_MENU(menu), reload_menu); + g_signal_connect(reload_menu, + "activate", + G_CALLBACK(OnReloadMenuActivatedThunk), + this); + + GtkWidget* navigate_separator = gtk_separator_menu_item_new(); + gtk_menu_append(GTK_MENU(menu), navigate_separator); + } + + if (has_link) { + GtkWidget* open_menu = gtk_menu_item_new_with_label("Open in New Window"); + gtk_menu_append(GTK_MENU(menu), open_menu); + g_signal_connect(open_menu, + "activate", + G_CALLBACK(OnOpenURLMenuActivatedThunk), + this); + + GtkWidget* link_separator = gtk_separator_menu_item_new(); + gtk_menu_append(GTK_MENU(menu), link_separator); + } + + if (params_.is_editable) { + GtkWidget* cut_menu = gtk_menu_item_new_with_label("Cut"); + gtk_menu_append(GTK_MENU(menu), cut_menu); + g_signal_connect(cut_menu, + "activate", + G_CALLBACK(OnCutMenuActivatedThunk), + this); + gtk_widget_set_sensitive( + cut_menu, + params_.edit_flags & WebContextMenuData::CanCut); + + GtkWidget* copy_menu = gtk_menu_item_new_with_label("Copy"); + gtk_menu_append(GTK_MENU(menu), copy_menu); + g_signal_connect(copy_menu, + "activate", + G_CALLBACK(OnCopyMenuActivatedThunk), + this); + gtk_widget_set_sensitive( + copy_menu, + params_.edit_flags & WebContextMenuData::CanCopy); + + GtkWidget* paste_menu = gtk_menu_item_new_with_label("Paste"); + gtk_menu_append(GTK_MENU(menu), paste_menu); + g_signal_connect(paste_menu, + "activate", + G_CALLBACK(OnPasteMenuActivatedThunk), + this); + gtk_widget_set_sensitive( + paste_menu, + params_.edit_flags & WebContextMenuData::CanPaste); + + GtkWidget* delete_menu = gtk_menu_item_new_with_label("Delete"); + gtk_menu_append(GTK_MENU(menu), delete_menu); + g_signal_connect(delete_menu, + "activate", + G_CALLBACK(OnDeleteMenuActivatedThunk), + this); + gtk_widget_set_sensitive( + delete_menu, + params_.edit_flags & WebContextMenuData::CanDelete); + + GtkWidget* edit_separator = gtk_separator_menu_item_new(); + gtk_menu_append(GTK_MENU(menu), edit_separator); + } else if (has_selection) { + GtkWidget* copy_menu = gtk_menu_item_new_with_label("Copy"); + gtk_menu_append(GTK_MENU(menu), copy_menu); + g_signal_connect(copy_menu, + "activate", + G_CALLBACK(OnCopyMenuActivatedThunk), + this); + + GtkWidget* copy_separator = gtk_separator_menu_item_new(); + gtk_menu_append(GTK_MENU(menu), copy_separator); + } + + GtkWidget* inspect_menu = gtk_menu_item_new_with_label("Inspect..."); + gtk_menu_append(GTK_MENU(menu), inspect_menu); + g_signal_connect(inspect_menu, + "activate", + G_CALLBACK(OnInspectMenuActivatedThunk), + this); + + gtk_widget_show_all(menu); + + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, GDK_CURRENT_TIME); +} + +WebDragDestDelegate* ShellWebContentsViewDelegate::GetDragDestDelegate() { + return NULL; +} + +void ShellWebContentsViewDelegate::Initialize(GtkWidget* expanded_container, + ui::FocusStoreGtk* focus_store) { + expanded_container_ = expanded_container; + + gtk_container_add(GTK_CONTAINER(floating_.get()), expanded_container_); + gtk_widget_show(floating_.get()); +} + +gfx::NativeView ShellWebContentsViewDelegate::GetNativeView() const { + return floating_.get(); +} + +void ShellWebContentsViewDelegate::Focus() { + GtkWidget* widget = web_contents_->GetView()->GetContentNativeView(); + if (widget) + gtk_widget_grab_focus(widget); +} + +gboolean ShellWebContentsViewDelegate::OnNativeViewFocusEvent( + GtkWidget* widget, + GtkDirectionType type, + gboolean* return_value) { + return false; +} + +void ShellWebContentsViewDelegate::OnBackMenuActivated(GtkWidget* widget) { + web_contents_->GetController().GoToOffset(-1); + web_contents_->GetView()->Focus(); +} + +void ShellWebContentsViewDelegate::OnForwardMenuActivated(GtkWidget* widget) { + web_contents_->GetController().GoToOffset(1); + web_contents_->GetView()->Focus(); +} + +void ShellWebContentsViewDelegate::OnReloadMenuActivated(GtkWidget* widget) { + web_contents_->GetController().Reload(false); + web_contents_->GetView()->Focus(); +} + +void ShellWebContentsViewDelegate::OnOpenURLMenuActivated(GtkWidget* widget) { + ShellBrowserContext* browser_context = + ShellContentBrowserClient::Get()->browser_context(); + Shell::CreateNewWindow(browser_context, + params_.link_url, + NULL, + MSG_ROUTING_NONE, + gfx::Size()); +} + +void ShellWebContentsViewDelegate::OnCutMenuActivated(GtkWidget* widget) { + web_contents_->GetRenderViewHost()->Cut(); +} + +void ShellWebContentsViewDelegate::OnCopyMenuActivated(GtkWidget* widget) { + web_contents_->GetRenderViewHost()->Copy(); +} + +void ShellWebContentsViewDelegate::OnPasteMenuActivated(GtkWidget* widget) { + web_contents_->GetRenderViewHost()->Paste(); +} + +void ShellWebContentsViewDelegate::OnDeleteMenuActivated(GtkWidget* widget) { + web_contents_->GetRenderViewHost()->Delete(); +} + +void ShellWebContentsViewDelegate::OnInspectMenuActivated(GtkWidget* widget) { + ShellDevToolsFrontend::Show(web_contents_); +} + +} // namespace content diff --git a/content/shell/browser/shell_web_contents_view_delegate_mac.mm b/content/shell/browser/shell_web_contents_view_delegate_mac.mm new file mode 100644 index 0000000..00196af --- /dev/null +++ b/content/shell/browser/shell_web_contents_view_delegate_mac.mm @@ -0,0 +1,277 @@ +// 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 "content/shell/browser/shell_web_contents_view_delegate.h" + +#import <Cocoa/Cocoa.h> + +#include "base/command_line.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "content/public/common/context_menu_params.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/shell/browser/shell_browser_main_parts.h" +#include "content/shell/browser/shell_content_browser_client.h" +#include "content/shell/browser/shell_devtools_frontend.h" +#include "content/shell/browser/shell_web_contents_view_delegate_creator.h" +#include "content/shell/common/shell_switches.h" +#include "third_party/WebKit/public/web/WebContextMenuData.h" + +using WebKit::WebContextMenuData; + +enum { + ShellContextMenuItemCutTag = 0, + ShellContextMenuItemCopyTag, + ShellContextMenuItemPasteTag, + ShellContextMenuItemDeleteTag, + ShellContextMenuItemOpenLinkTag, + ShellContextMenuItemBackTag, + ShellContextMenuItemForwardTag, + ShellContextMenuItemReloadTag, + ShellContextMenuItemInspectTag +}; + +@interface ShellContextMenuDelegate : NSObject<NSMenuDelegate> { + @private + content::ShellWebContentsViewDelegate* delegate_; +} +@end + +@implementation ShellContextMenuDelegate +- (id)initWithDelegate:(content::ShellWebContentsViewDelegate*) delegate { + if ((self = [super init])) { + delegate_ = delegate; + } + return self; +} + +- (void)itemSelected:(id)sender { + NSInteger tag = [sender tag]; + delegate_->ActionPerformed(tag); +} +@end + +namespace { + +NSMenuItem* MakeContextMenuItem(NSString* title, + NSInteger tag, + NSMenu* menu, + BOOL enabled, + ShellContextMenuDelegate* delegate) { + NSMenuItem* menu_item = + [[NSMenuItem alloc] initWithTitle:title + action:@selector(itemSelected:) + keyEquivalent:@""]; + [menu_item setTarget:delegate]; + [menu_item setTag:tag]; + [menu_item setEnabled:enabled]; + [menu addItem:menu_item]; + + return menu_item; +} + +} // namespace + +namespace content { + +WebContentsViewDelegate* CreateShellWebContentsViewDelegate( + WebContents* web_contents) { + return new ShellWebContentsViewDelegate(web_contents); +} + +ShellWebContentsViewDelegate::ShellWebContentsViewDelegate( + WebContents* web_contents) + : web_contents_(web_contents) { +} + +ShellWebContentsViewDelegate::~ShellWebContentsViewDelegate() { +} + +void ShellWebContentsViewDelegate::ShowContextMenu( + const ContextMenuParams& params) { + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) + return; + + params_ = params; + bool has_link = !params_.unfiltered_link_url.is_empty(); + bool has_selection = ! params_.selection_text.empty(); + + NSMenu* menu = [[[NSMenu alloc] initWithTitle:@""] autorelease]; + ShellContextMenuDelegate* delegate = + [[ShellContextMenuDelegate alloc] initWithDelegate:this]; + [menu setDelegate:delegate]; + [menu setAutoenablesItems:NO]; + + if (params.media_type == WebContextMenuData::MediaTypeNone && + !has_link && + !has_selection && + !params_.is_editable) { + BOOL back_menu_enabled = + web_contents_->GetController().CanGoBack() ? YES : NO; + MakeContextMenuItem(@"Back", + ShellContextMenuItemBackTag, + menu, + back_menu_enabled, + delegate); + + BOOL forward_menu_enabled = + web_contents_->GetController().CanGoForward() ? YES : NO; + MakeContextMenuItem(@"Forward", + ShellContextMenuItemForwardTag, + menu, + forward_menu_enabled, + delegate); + + MakeContextMenuItem(@"Reload", + ShellContextMenuItemReloadTag, + menu, + YES, + delegate); + + NSMenuItem* separator = [NSMenuItem separatorItem]; + [menu addItem:separator]; + } + + if (has_link) { + MakeContextMenuItem(@"Open In New Window", + ShellContextMenuItemOpenLinkTag, + menu, + YES, + delegate); + + NSMenuItem* separator = [NSMenuItem separatorItem]; + [menu addItem:separator]; + } + + if (params_.is_editable) { + BOOL cut_menu_enabled = + (params_.edit_flags & WebContextMenuData::CanCut) ? YES : NO; + MakeContextMenuItem(@"Cut", + ShellContextMenuItemCutTag, + menu, + cut_menu_enabled, + delegate); + + BOOL copy_menu_enabled = + (params_.edit_flags & WebContextMenuData::CanCopy) ? YES : NO; + MakeContextMenuItem(@"Copy", + ShellContextMenuItemCopyTag, + menu, + copy_menu_enabled, + delegate); + + BOOL paste_menu_enabled = + (params_.edit_flags & WebContextMenuData::CanPaste) ? YES : NO; + MakeContextMenuItem(@"Paste", + ShellContextMenuItemPasteTag, + menu, + paste_menu_enabled, + delegate); + + BOOL delete_menu_enabled = + (params_.edit_flags & WebContextMenuData::CanDelete) ? YES : NO; + MakeContextMenuItem(@"Delete", + ShellContextMenuItemDeleteTag, + menu, + delete_menu_enabled, + delegate); + + NSMenuItem* separator = [NSMenuItem separatorItem]; + [menu addItem:separator]; + } else if (has_selection) { + MakeContextMenuItem(@"Copy", + ShellContextMenuItemCopyTag, + menu, + YES, + delegate); + + NSMenuItem* separator = [NSMenuItem separatorItem]; + [menu addItem:separator]; + } + + MakeContextMenuItem(@"Inspect", + ShellContextMenuItemInspectTag, + menu, + YES, + delegate); + + NSView* parent_view = web_contents_->GetView()->GetContentNativeView(); + NSEvent* currentEvent = [NSApp currentEvent]; + NSWindow* window = [parent_view window]; + NSPoint position = [window mouseLocationOutsideOfEventStream]; + NSTimeInterval eventTime = [currentEvent timestamp]; + NSEvent* clickEvent = [NSEvent mouseEventWithType:NSRightMouseDown + location:position + modifierFlags:NSRightMouseDownMask + timestamp:eventTime + windowNumber:[window windowNumber] + context:nil + eventNumber:0 + clickCount:1 + pressure:1.0]; + + [NSMenu popUpContextMenu:menu + withEvent:clickEvent + forView:parent_view]; +} + +void ShellWebContentsViewDelegate::ActionPerformed(int tag) { + switch (tag) { + case ShellContextMenuItemCutTag: + web_contents_->GetRenderViewHost()->Cut(); + break; + case ShellContextMenuItemCopyTag: + web_contents_->GetRenderViewHost()->Copy(); + break; + case ShellContextMenuItemPasteTag: + web_contents_->GetRenderViewHost()->Paste(); + break; + case ShellContextMenuItemDeleteTag: + web_contents_->GetRenderViewHost()->Delete(); + break; + case ShellContextMenuItemOpenLinkTag: { + ShellBrowserContext* browser_context = + ShellContentBrowserClient::Get()->browser_context(); + Shell::CreateNewWindow(browser_context, + params_.link_url, + NULL, + MSG_ROUTING_NONE, + gfx::Size()); + break; + } + case ShellContextMenuItemBackTag: + web_contents_->GetController().GoToOffset(-1); + web_contents_->GetView()->Focus(); + break; + case ShellContextMenuItemForwardTag: + web_contents_->GetController().GoToOffset(1); + web_contents_->GetView()->Focus(); + break; + case ShellContextMenuItemReloadTag: { + web_contents_->GetController().Reload(false); + web_contents_->GetView()->Focus(); + break; + } + case ShellContextMenuItemInspectTag: { + ShellDevToolsFrontend::Show(web_contents_); + break; + } + } +} + +WebDragDestDelegate* ShellWebContentsViewDelegate::GetDragDestDelegate() { + return NULL; +} + +NSObject<RenderWidgetHostViewMacDelegate>* +ShellWebContentsViewDelegate::CreateRenderWidgetHostViewDelegate( + content::RenderWidgetHost* render_widget_host) { + return NULL; +} + +} // namespace content diff --git a/content/shell/browser/shell_web_contents_view_delegate_win.cc b/content/shell/browser/shell_web_contents_view_delegate_win.cc new file mode 100644 index 0000000..cd387f0 --- /dev/null +++ b/content/shell/browser/shell_web_contents_view_delegate_win.cc @@ -0,0 +1,254 @@ +// 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 "content/shell/browser/shell_web_contents_view_delegate.h" + +#include "base/command_line.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "content/public/common/context_menu_params.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/shell/browser/shell_browser_main_parts.h" +#include "content/shell/browser/shell_content_browser_client.h" +#include "content/shell/browser/shell_devtools_frontend.h" +#include "content/shell/browser/shell_web_contents_view_delegate_creator.h" +#include "content/shell/common/shell_switches.h" +#include "third_party/WebKit/public/web/WebContextMenuData.h" + +using WebKit::WebContextMenuData; + +namespace { + +enum { + ShellContextMenuItemCutId = 10001, + ShellContextMenuItemCopyId, + ShellContextMenuItemPasteId, + ShellContextMenuItemDeleteId, + ShellContextMenuItemOpenLinkId, + ShellContextMenuItemBackId, + ShellContextMenuItemForwardId, + ShellContextMenuItemReloadId, + ShellContextMenuItemInspectId +}; + +void MakeContextMenuItem(HMENU menu, + int menu_index, + LPTSTR text, + UINT id, + bool enabled) { + MENUITEMINFO mii = {0}; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_DATA | MIIM_STRING | MIIM_STATE; + mii.fState = enabled ? MFS_ENABLED : (MF_DISABLED | MFS_GRAYED); + mii.fType = MFT_STRING; + mii.wID = id; + mii.dwTypeData = text; + + InsertMenuItem(menu, menu_index, TRUE, &mii); +} + +} // namespace + +namespace content { + +WebContentsViewDelegate* CreateShellWebContentsViewDelegate( + WebContents* web_contents) { + return new ShellWebContentsViewDelegate(web_contents); +} + +ShellWebContentsViewDelegate::ShellWebContentsViewDelegate( + WebContents* web_contents) + : web_contents_(web_contents) { +} + +ShellWebContentsViewDelegate::~ShellWebContentsViewDelegate() { +} + +void ShellWebContentsViewDelegate::ShowContextMenu( + const ContextMenuParams& params) { + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) + return; + + params_ = params; + bool has_link = !params_.unfiltered_link_url.is_empty(); + bool has_selection = !params_.selection_text.empty(); + + HMENU menu = CreateMenu(); + HMENU sub_menu = CreatePopupMenu(); + AppendMenu(menu, MF_STRING | MF_POPUP, (UINT)sub_menu, L""); + + int index = 0; + if (params_.media_type == WebContextMenuData::MediaTypeNone && + !has_link && + !has_selection && + !params_.is_editable) { + MakeContextMenuItem(sub_menu, + index++, + L"Back", + ShellContextMenuItemBackId, + web_contents_->GetController().CanGoBack()); + + MakeContextMenuItem(sub_menu, + index++, + L"Forward", + ShellContextMenuItemForwardId, + web_contents_->GetController().CanGoForward()); + + MakeContextMenuItem(sub_menu, + index++, + L"Reload", + ShellContextMenuItemReloadId, + true); + + AppendMenu(sub_menu, MF_SEPARATOR, 0, NULL); + index++; + } + + if (has_link) { + MakeContextMenuItem(sub_menu, + index++, + L"Open in New Window", + ShellContextMenuItemOpenLinkId, + true); + AppendMenu(sub_menu, MF_SEPARATOR, 0, NULL); + index++; + } + + if (params_.is_editable) { + bool cut_enabled = ((params_.edit_flags & WebContextMenuData::CanCut) != 0); + MakeContextMenuItem(sub_menu, + index++, + L"Cut", + ShellContextMenuItemCutId, + cut_enabled); + + bool copy_enabled = + ((params_.edit_flags & WebContextMenuData::CanCopy) != 0); + MakeContextMenuItem(sub_menu, + index++, + L"Copy", + ShellContextMenuItemCopyId, + copy_enabled); + + bool paste_enabled = + ((params_.edit_flags & WebContextMenuData::CanPaste) != 0); + MakeContextMenuItem(sub_menu, + index++, + L"Paste", + ShellContextMenuItemPasteId, + paste_enabled); + bool delete_enabled = + ((params_.edit_flags & WebContextMenuData::CanDelete) != 0); + MakeContextMenuItem(sub_menu, + index++, + L"Delete", + ShellContextMenuItemDeleteId, + delete_enabled); + + AppendMenu(sub_menu, MF_SEPARATOR, 0, NULL); + index++; + } else if (has_selection) { + MakeContextMenuItem(sub_menu, + index++, + L"Copy", + ShellContextMenuItemCopyId, + true); + + AppendMenu(sub_menu, MF_SEPARATOR, 0, NULL); + index++; + } + + MakeContextMenuItem(sub_menu, + index++, + L"Inspect...", + ShellContextMenuItemInspectId, + true); +#if defined(USE_AURA) + NOTIMPLEMENTED(); +#else + gfx::Point screen_point(params.x, params.y); + POINT point = screen_point.ToPOINT(); + ClientToScreen(web_contents_->GetView()->GetNativeView(), &point); + + int selection = + TrackPopupMenu(sub_menu, + TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, + point.x, point.y, + 0, + web_contents_->GetView()->GetContentNativeView(), + NULL); + + MenuItemSelected(selection); +#endif + DestroyMenu(menu); +} + +void ShellWebContentsViewDelegate::MenuItemSelected(int selection) { + switch (selection) { + case ShellContextMenuItemCutId: + web_contents_->GetRenderViewHost()->Cut(); + break; + case ShellContextMenuItemCopyId: + web_contents_->GetRenderViewHost()->Copy(); + break; + case ShellContextMenuItemPasteId: + web_contents_->GetRenderViewHost()->Paste(); + break; + case ShellContextMenuItemDeleteId: + web_contents_->GetRenderViewHost()->Delete(); + break; + case ShellContextMenuItemOpenLinkId: { + ShellBrowserContext* browser_context = + ShellContentBrowserClient::Get()->browser_context(); + Shell::CreateNewWindow(browser_context, + params_.link_url, + NULL, + MSG_ROUTING_NONE, + gfx::Size()); + break; + } + case ShellContextMenuItemBackId: + web_contents_->GetController().GoToOffset(-1); + web_contents_->GetView()->Focus(); + break; + case ShellContextMenuItemForwardId: + web_contents_->GetController().GoToOffset(1); + web_contents_->GetView()->Focus(); + break; + case ShellContextMenuItemReloadId: + web_contents_->GetController().Reload(false); + web_contents_->GetView()->Focus(); + break; + case ShellContextMenuItemInspectId: { + ShellDevToolsFrontend::Show(web_contents_); + break; + } + } +} + +WebDragDestDelegate* ShellWebContentsViewDelegate::GetDragDestDelegate() { + return NULL; +} + +void ShellWebContentsViewDelegate::StoreFocus() { +} + +void ShellWebContentsViewDelegate::RestoreFocus() { +} + +bool ShellWebContentsViewDelegate::Focus() { + return false; +} + +void ShellWebContentsViewDelegate::TakeFocus(bool reverse) { +} + +void ShellWebContentsViewDelegate::SizeChanged(const gfx::Size& size) { +} + +} // namespace content diff --git a/content/shell/browser/shell_win.cc b/content/shell/browser/shell_win.cc new file mode 100644 index 0000000..d1bb402 --- /dev/null +++ b/content/shell/browser/shell_win.cc @@ -0,0 +1,285 @@ +// 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 "content/shell/browser/shell.h" + +#include <windows.h> +#include <commctrl.h> +#include <fcntl.h> +#include <io.h> + +#include "base/strings/utf_string_conversions.h" +#include "base/win/wrapped_window_proc.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "content/shell/app/resource.h" +#include "ui/base/win/hwnd_util.h" + +namespace { + +const wchar_t kWindowTitle[] = L"Content Shell"; +const wchar_t kWindowClass[] = L"CONTENT_SHELL"; + +const int kButtonWidth = 72; +const int kURLBarHeight = 24; + +const int kMaxURLLength = 1024; + +} // namespace + +namespace content { + +HINSTANCE Shell::instance_handle_; + +void Shell::PlatformInitialize(const gfx::Size& default_window_size) { + _setmode(_fileno(stdout), _O_BINARY); + _setmode(_fileno(stderr), _O_BINARY); + INITCOMMONCONTROLSEX InitCtrlEx; + InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX); + InitCtrlEx.dwICC = ICC_STANDARD_CLASSES; + InitCommonControlsEx(&InitCtrlEx); + RegisterWindowClass(); +} + +void Shell::PlatformExit() { + std::vector<Shell*> windows = windows_; + for (std::vector<Shell*>::iterator it = windows.begin(); + it != windows.end(); ++it) + DestroyWindow((*it)->window_); +} + +void Shell::PlatformCleanUp() { + // When the window is destroyed, tell the Edit field to forget about us, + // otherwise we will crash. + ui::SetWindowProc(url_edit_view_, default_edit_wnd_proc_); + ui::SetWindowUserData(url_edit_view_, NULL); +} + +void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) { + int id; + switch (control) { + case BACK_BUTTON: + id = IDC_NAV_BACK; + break; + case FORWARD_BUTTON: + id = IDC_NAV_FORWARD; + break; + case STOP_BUTTON: + id = IDC_NAV_STOP; + break; + default: + NOTREACHED() << "Unknown UI control"; + return; + } + EnableWindow(GetDlgItem(window_, id), is_enabled); +} + +void Shell::PlatformSetAddressBarURL(const GURL& url) { + std::wstring url_string = UTF8ToWide(url.spec()); + SendMessage(url_edit_view_, WM_SETTEXT, 0, + reinterpret_cast<LPARAM>(url_string.c_str())); +} + +void Shell::PlatformSetIsLoading(bool loading) { +} + +void Shell::PlatformCreateWindow(int width, int height) { + window_ = CreateWindow(kWindowClass, kWindowTitle, + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, + NULL, NULL, instance_handle_, NULL); + ui::SetWindowUserData(window_, this); + + HWND hwnd; + int x = 0; + + hwnd = CreateWindow(L"BUTTON", L"Back", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON , + x, 0, kButtonWidth, kURLBarHeight, + window_, (HMENU) IDC_NAV_BACK, instance_handle_, 0); + x += kButtonWidth; + + hwnd = CreateWindow(L"BUTTON", L"Forward", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON , + x, 0, kButtonWidth, kURLBarHeight, + window_, (HMENU) IDC_NAV_FORWARD, instance_handle_, 0); + x += kButtonWidth; + + hwnd = CreateWindow(L"BUTTON", L"Reload", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON , + x, 0, kButtonWidth, kURLBarHeight, + window_, (HMENU) IDC_NAV_RELOAD, instance_handle_, 0); + x += kButtonWidth; + + hwnd = CreateWindow(L"BUTTON", L"Stop", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON , + x, 0, kButtonWidth, kURLBarHeight, + window_, (HMENU) IDC_NAV_STOP, instance_handle_, 0); + x += kButtonWidth; + + // This control is positioned by PlatformResizeSubViews. + url_edit_view_ = CreateWindow(L"EDIT", 0, + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | + ES_AUTOVSCROLL | ES_AUTOHSCROLL, + x, 0, 0, 0, window_, 0, instance_handle_, 0); + + default_edit_wnd_proc_ = ui::SetWindowProc(url_edit_view_, + Shell::EditWndProc); + ui::SetWindowUserData(url_edit_view_, this); + + ShowWindow(window_, SW_SHOW); + + SizeTo(width, height); +} + +void Shell::PlatformSetContents() { + SetParent(web_contents_->GetView()->GetNativeView(), window_); +} + +void Shell::SizeTo(int width, int height) { + RECT rc, rw; + GetClientRect(window_, &rc); + GetWindowRect(window_, &rw); + + int client_width = rc.right - rc.left; + int window_width = rw.right - rw.left; + window_width = (window_width - client_width) + width; + + int client_height = rc.bottom - rc.top; + int window_height = rw.bottom - rw.top; + window_height = (window_height - client_height) + height; + + // Add space for the url bar. + window_height += kURLBarHeight; + + SetWindowPos(window_, NULL, 0, 0, window_width, window_height, + SWP_NOMOVE | SWP_NOZORDER); +} + +void Shell::PlatformResizeSubViews() { + RECT rc; + GetClientRect(window_, &rc); + + int x = kButtonWidth * 4; + MoveWindow(url_edit_view_, x, 0, rc.right - x, kURLBarHeight, TRUE); + + MoveWindow(GetContentView(), 0, kURLBarHeight, rc.right, + rc.bottom - kURLBarHeight, TRUE); +} + +void Shell::Close() { + DestroyWindow(window_); +} + +ATOM Shell::RegisterWindowClass() { + WNDCLASSEX window_class; + base::win::InitializeWindowClass( + kWindowClass, + &Shell::WndProc, + CS_HREDRAW | CS_VREDRAW, + 0, + 0, + LoadCursor(NULL, IDC_ARROW), + NULL, + MAKEINTRESOURCE(IDC_CONTENTSHELL), + NULL, + NULL, + &window_class); + instance_handle_ = window_class.hInstance; + return RegisterClassEx(&window_class); +} + +LRESULT CALLBACK Shell::WndProc(HWND hwnd, UINT message, WPARAM wParam, + LPARAM lParam) { + Shell* shell = static_cast<Shell*>(ui::GetWindowUserData(hwnd)); + + switch (message) { + case WM_COMMAND: { + int id = LOWORD(wParam); + switch (id) { + case IDM_NEW_WINDOW: + CreateNewWindow( + shell->web_contents()->GetBrowserContext(), + GURL(), NULL, MSG_ROUTING_NONE, gfx::Size()); + break; + case IDM_CLOSE_WINDOW: + DestroyWindow(hwnd); + break; + case IDM_EXIT: + PlatformExit(); + break; + case IDM_SHOW_DEVELOPER_TOOLS: + shell->ShowDevTools(); + break; + case IDC_NAV_BACK: + shell->GoBackOrForward(-1); + break; + case IDC_NAV_FORWARD: + shell->GoBackOrForward(1); + break; + case IDC_NAV_RELOAD: + shell->Reload(); + break; + case IDC_NAV_STOP: + shell->Stop(); + break; + } + break; + } + case WM_DESTROY: { + delete shell; + return 0; + } + + case WM_SIZE: { + if (shell->GetContentView()) + shell->PlatformResizeSubViews(); + return 0; + } + + case WM_WINDOWPOSCHANGED: { + // Notify the content view that the window position of its parent window + // has been changed by sending window message + gfx::NativeView native_view = shell->GetContentView(); + if (native_view) { + SendMessage(native_view, message, wParam, lParam); + } + break; + } + } + + return DefWindowProc(hwnd, message, wParam, lParam); +} + +LRESULT CALLBACK Shell::EditWndProc(HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam) { + Shell* shell = static_cast<Shell*>(ui::GetWindowUserData(hwnd)); + + switch (message) { + case WM_CHAR: + if (wParam == VK_RETURN) { + wchar_t str[kMaxURLLength + 1]; // Leave room for adding a NULL; + *(str) = kMaxURLLength; + LRESULT str_len = SendMessage(hwnd, EM_GETLINE, 0, (LPARAM)str); + if (str_len > 0) { + str[str_len] = 0; // EM_GETLINE doesn't NULL terminate. + GURL url(str); + if (!url.has_scheme()) + url = GURL(std::wstring(L"http://") + std::wstring(str)); + shell->LoadURL(url); + } + + return 0; + } + } + + return CallWindowProc(shell->default_edit_wnd_proc_, hwnd, message, wParam, + lParam); +} + +void Shell::PlatformSetTitle(const string16& text) { + ::SetWindowText(window_, text.c_str()); +} + +} // namespace content diff --git a/content/shell/browser/webkit_test_controller.cc b/content/shell/browser/webkit_test_controller.cc new file mode 100644 index 0000000..40ed9c5 --- /dev/null +++ b/content/shell/browser/webkit_test_controller.cc @@ -0,0 +1,632 @@ +// 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 "content/shell/browser/webkit_test_controller.h" + +#include <iostream> + +#include "base/base64.h" +#include "base/command_line.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "content/public/browser/devtools_manager.h" +#include "content/public/browser/gpu_data_manager.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_types.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_view.h" +#include "content/public/common/content_switches.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/shell/browser/shell_content_browser_client.h" +#include "content/shell/common/shell_messages.h" +#include "content/shell/common/shell_switches.h" +#include "content/shell/common/webkit_test_helpers.h" +#include "ui/gfx/codec/png_codec.h" + +namespace content { + +const int kTestSVGWindowWidthDip = 480; +const int kTestSVGWindowHeightDip = 360; + +// WebKitTestResultPrinter ---------------------------------------------------- + +WebKitTestResultPrinter::WebKitTestResultPrinter( + std::ostream* output, std::ostream* error) + : state_(DURING_TEST), + capture_text_only_(false), + encode_binary_data_(false), + output_(output), + error_(error) { +} + +WebKitTestResultPrinter::~WebKitTestResultPrinter() { +} + +void WebKitTestResultPrinter::PrintTextHeader() { + if (state_ != DURING_TEST) + return; + if (!capture_text_only_) + *output_ << "Content-Type: text/plain\n"; + state_ = IN_TEXT_BLOCK; +} + +void WebKitTestResultPrinter::PrintTextBlock(const std::string& block) { + if (state_ != IN_TEXT_BLOCK) + return; + *output_ << block; +} + +void WebKitTestResultPrinter::PrintTextFooter() { + if (state_ != IN_TEXT_BLOCK) + return; + if (!capture_text_only_) { + *output_ << "#EOF\n"; + output_->flush(); + } + state_ = IN_IMAGE_BLOCK; +} + +void WebKitTestResultPrinter::PrintImageHeader( + const std::string& actual_hash, + const std::string& expected_hash) { + if (state_ != IN_IMAGE_BLOCK || capture_text_only_) + return; + *output_ << "\nActualHash: " << actual_hash << "\n"; + if (!expected_hash.empty()) + *output_ << "\nExpectedHash: " << expected_hash << "\n"; +} + +void WebKitTestResultPrinter::PrintImageBlock( + const std::vector<unsigned char>& png_image) { + if (state_ != IN_IMAGE_BLOCK || capture_text_only_) + return; + *output_ << "Content-Type: image/png\n"; + if (encode_binary_data_) { + PrintEncodedBinaryData(png_image); + return; + } + + *output_ << "Content-Length: " << png_image.size() << "\n"; + output_->write( + reinterpret_cast<const char*>(&png_image[0]), png_image.size()); +} + +void WebKitTestResultPrinter::PrintImageFooter() { + if (state_ != IN_IMAGE_BLOCK) + return; + if (!capture_text_only_) { + *output_ << "#EOF\n"; + *error_ << "#EOF\n"; + output_->flush(); + error_->flush(); + } + state_ = AFTER_TEST; +} + +void WebKitTestResultPrinter::PrintAudioHeader() { + DCHECK_EQ(state_, DURING_TEST); + if (!capture_text_only_) + *output_ << "Content-Type: audio/wav\n"; + state_ = IN_AUDIO_BLOCK; +} + +void WebKitTestResultPrinter::PrintAudioBlock( + const std::vector<unsigned char>& audio_data) { + if (state_ != IN_AUDIO_BLOCK || capture_text_only_) + return; + if (encode_binary_data_) { + PrintEncodedBinaryData(audio_data); + return; + } + + *output_ << "Content-Length: " << audio_data.size() << "\n"; + output_->write( + reinterpret_cast<const char*>(&audio_data[0]), audio_data.size()); +} + +void WebKitTestResultPrinter::PrintAudioFooter() { + if (state_ != IN_AUDIO_BLOCK) + return; + if (!capture_text_only_) { + *output_ << "#EOF\n"; + *error_ << "#EOF\n"; + output_->flush(); + error_->flush(); + } + state_ = IN_IMAGE_BLOCK; +} + +void WebKitTestResultPrinter::AddMessage(const std::string& message) { + AddMessageRaw(message + "\n"); +} + +void WebKitTestResultPrinter::AddMessageRaw(const std::string& message) { + if (state_ != DURING_TEST) + return; + *output_ << message; +} + +void WebKitTestResultPrinter::AddErrorMessage(const std::string& message) { + if (!capture_text_only_) + *error_ << message << "\n"; + if (state_ != DURING_TEST) + return; + PrintTextHeader(); + *output_ << message << "\n"; + PrintTextFooter(); + PrintImageFooter(); +} + +void WebKitTestResultPrinter::PrintEncodedBinaryData( + const std::vector<unsigned char>& data) { + *output_ << "Content-Transfer-Encoding: base64\n"; + + std::string data_base64; + const bool success = base::Base64Encode( + base::StringPiece(reinterpret_cast<const char*>(&data[0]), data.size()), + &data_base64); + DCHECK(success); + + *output_ << "Content-Length: " << data_base64.length() << "\n"; + output_->write(data_base64.c_str(), data_base64.length()); +} + + +// WebKitTestController ------------------------------------------------------- + +WebKitTestController* WebKitTestController::instance_ = NULL; + +// static +WebKitTestController* WebKitTestController::Get() { + DCHECK(instance_); + return instance_; +} + +WebKitTestController::WebKitTestController() + : main_window_(NULL), + test_phase_(BETWEEN_TESTS) { + CHECK(!instance_); + instance_ = this; + printer_.reset(new WebKitTestResultPrinter(&std::cout, &std::cerr)); + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEncodeBinary)) + printer_->set_encode_binary_data(true); + registrar_.Add(this, + NOTIFICATION_RENDERER_PROCESS_CREATED, + NotificationService::AllSources()); + GpuDataManager::GetInstance()->AddObserver(this); + ResetAfterLayoutTest(); +} + +WebKitTestController::~WebKitTestController() { + DCHECK(CalledOnValidThread()); + CHECK(instance_ == this); + CHECK(test_phase_ == BETWEEN_TESTS); + GpuDataManager::GetInstance()->RemoveObserver(this); + DiscardMainWindow(); + instance_ = NULL; +} + +bool WebKitTestController::PrepareForLayoutTest( + const GURL& test_url, + const base::FilePath& current_working_directory, + bool enable_pixel_dumping, + const std::string& expected_pixel_hash) { + DCHECK(CalledOnValidThread()); + test_phase_ = DURING_TEST; + current_working_directory_ = current_working_directory; + enable_pixel_dumping_ = enable_pixel_dumping; + expected_pixel_hash_ = expected_pixel_hash; + test_url_ = test_url; + printer_->reset(); + ShellBrowserContext* browser_context = + ShellContentBrowserClient::Get()->browser_context(); + if (test_url.spec().find("compositing/") != std::string::npos) + is_compositing_test_ = true; + initial_size_ = gfx::Size( + Shell::kDefaultTestWindowWidthDip, Shell::kDefaultTestWindowHeightDip); + // The W3C SVG layout tests use a different size than the other layout tests. + if (test_url.spec().find("W3C-SVG-1.1") != std::string::npos) + initial_size_ = gfx::Size(kTestSVGWindowWidthDip, kTestSVGWindowHeightDip); + if (!main_window_) { + main_window_ = content::Shell::CreateNewWindow( + browser_context, + GURL(), + NULL, + MSG_ROUTING_NONE, + initial_size_); + WebContentsObserver::Observe(main_window_->web_contents()); + send_configuration_to_next_host_ = true; + current_pid_ = base::kNullProcessId; + main_window_->LoadURL(test_url); + } else { +#if (defined(OS_WIN) && !defined(USE_AURA)) || \ + defined(TOOLKIT_GTK) || defined(OS_MACOSX) + // Shell::SizeTo is not implemented on all platforms. + main_window_->SizeTo(initial_size_.width(), initial_size_.height()); +#endif + main_window_->web_contents()->GetRenderViewHost()->GetView() + ->SetSize(initial_size_); + main_window_->web_contents()->GetRenderViewHost()->WasResized(); + RenderViewHost* render_view_host = + main_window_->web_contents()->GetRenderViewHost(); + WebPreferences prefs = render_view_host->GetWebkitPreferences(); + OverrideWebkitPrefs(&prefs); + render_view_host->UpdateWebkitPreferences(prefs); + SendTestConfiguration(); + + NavigationController::LoadURLParams params(test_url); + params.transition_type = PageTransitionFromInt( + PAGE_TRANSITION_TYPED | PAGE_TRANSITION_FROM_ADDRESS_BAR); + params.should_clear_history_list = true; + main_window_->web_contents()->GetController().LoadURLWithParams(params); + main_window_->web_contents()->GetView()->Focus(); + } + main_window_->web_contents()->GetRenderViewHost()->SetActive(true); + main_window_->web_contents()->GetRenderViewHost()->Focus(); + return true; +} + +bool WebKitTestController::ResetAfterLayoutTest() { + DCHECK(CalledOnValidThread()); + printer_->PrintTextFooter(); + printer_->PrintImageFooter(); + send_configuration_to_next_host_ = false; + test_phase_ = BETWEEN_TESTS; + is_compositing_test_ = false; + enable_pixel_dumping_ = false; + expected_pixel_hash_.clear(); + test_url_ = GURL(); + prefs_ = WebPreferences(); + should_override_prefs_ = false; + return true; +} + +void WebKitTestController::SetTempPath(const base::FilePath& temp_path) { + temp_path_ = temp_path; +} + +void WebKitTestController::RendererUnresponsive() { + DCHECK(CalledOnValidThread()); + LOG(WARNING) << "renderer unresponsive"; +} + +void WebKitTestController::WorkerCrashed() { + DCHECK(CalledOnValidThread()); + printer_->AddErrorMessage("#CRASHED - worker"); + DiscardMainWindow(); +} + +void WebKitTestController::OverrideWebkitPrefs(WebPreferences* prefs) { + if (should_override_prefs_) { + *prefs = prefs_; + } else { + ApplyLayoutTestDefaultPreferences(prefs); + if (is_compositing_test_) { + CommandLine& command_line = *CommandLine::ForCurrentProcess(); + if (!command_line.HasSwitch(switches::kEnableSoftwareCompositing)) + prefs->accelerated_2d_canvas_enabled = true; + prefs->accelerated_compositing_for_video_enabled = true; + prefs->mock_scrollbars_enabled = true; + } + } +} + +void WebKitTestController::OpenURL(const GURL& url) { + if (test_phase_ != DURING_TEST) + return; + + Shell::CreateNewWindow(main_window_->web_contents()->GetBrowserContext(), + url, + main_window_->web_contents()->GetSiteInstance(), + MSG_ROUTING_NONE, + gfx::Size()); +} + +void WebKitTestController::TestFinishedInSecondaryWindow() { + RenderViewHost* render_view_host = + main_window_->web_contents()->GetRenderViewHost(); + render_view_host->Send( + new ShellViewMsg_NotifyDone(render_view_host->GetRoutingID())); +} + +bool WebKitTestController::IsMainWindow(WebContents* web_contents) const { + return main_window_ && web_contents == main_window_->web_contents(); +} + +bool WebKitTestController::OnMessageReceived(const IPC::Message& message) { + DCHECK(CalledOnValidThread()); + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(WebKitTestController, message) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_PrintMessage, OnPrintMessage) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_TextDump, OnTextDump) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_ImageDump, OnImageDump) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_AudioDump, OnAudioDump) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_OverridePreferences, + OnOverridePreferences) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_TestFinished, OnTestFinished) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_ShowDevTools, OnShowDevTools) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_CloseDevTools, OnCloseDevTools) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_GoToOffset, OnGoToOffset) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_Reload, OnReload) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_LoadURLForFrame, OnLoadURLForFrame) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_CaptureSessionHistory, + OnCaptureSessionHistory) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_CloseRemainingWindows, + OnCloseRemainingWindows) + IPC_MESSAGE_HANDLER(ShellViewHostMsg_ResetDone, OnResetDone) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; +} + +void WebKitTestController::PluginCrashed(const base::FilePath& plugin_path, + base::ProcessId plugin_pid) { + DCHECK(CalledOnValidThread()); + printer_->AddErrorMessage( + base::StringPrintf("#CRASHED - plugin (pid %d)", plugin_pid)); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(base::IgnoreResult(&WebKitTestController::DiscardMainWindow), + base::Unretained(this))); +} + +void WebKitTestController::RenderViewCreated(RenderViewHost* render_view_host) { + DCHECK(CalledOnValidThread()); + // Might be kNullProcessHandle, in which case we will receive a notification + // later when the RenderProcessHost was created. + if (render_view_host->GetProcess()->GetHandle() != base::kNullProcessHandle) + current_pid_ = base::GetProcId(render_view_host->GetProcess()->GetHandle()); + if (!send_configuration_to_next_host_) + return; + send_configuration_to_next_host_ = false; + SendTestConfiguration(); +} + +void WebKitTestController::RenderProcessGone(base::TerminationStatus status) { + DCHECK(CalledOnValidThread()); + if (current_pid_ != base::kNullProcessId) { + printer_->AddErrorMessage(std::string("#CRASHED - renderer (pid ") + + base::IntToString(current_pid_) + ")"); + } else { + printer_->AddErrorMessage("#CRASHED - renderer"); + } + DiscardMainWindow(); +} + +void WebKitTestController::WebContentsDestroyed(WebContents* web_contents) { + DCHECK(CalledOnValidThread()); + printer_->AddErrorMessage("FAIL: main window was destroyed"); + DiscardMainWindow(); +} + +void WebKitTestController::Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(CalledOnValidThread()); + switch (type) { + case NOTIFICATION_RENDERER_PROCESS_CREATED: { + if (!main_window_) + return; + RenderViewHost* render_view_host = + main_window_->web_contents()->GetRenderViewHost(); + if (!render_view_host) + return; + RenderProcessHost* render_process_host = + Source<RenderProcessHost>(source).ptr(); + if (render_process_host != render_view_host->GetProcess()) + return; + current_pid_ = base::GetProcId(render_process_host->GetHandle()); + break; + } + default: + NOTREACHED(); + } +} + +void WebKitTestController::OnGpuProcessCrashed( + base::TerminationStatus exit_code) { + DCHECK(CalledOnValidThread()); + printer_->AddErrorMessage("#CRASHED - gpu"); + DiscardMainWindow(); +} + +void WebKitTestController::TimeoutHandler() { + DCHECK(CalledOnValidThread()); + printer_->AddErrorMessage( + "FAIL: Timed out waiting for notifyDone to be called"); + DiscardMainWindow(); +} + +void WebKitTestController::DiscardMainWindow() { + // If we're running a test, we need to close all windows and exit the message + // loop. Otherwise, we're already outside of the message loop, and we just + // discard the main window. + WebContentsObserver::Observe(NULL); + if (test_phase_ != BETWEEN_TESTS) { + Shell::CloseAllWindows(); + base::MessageLoop::current()->PostTask(FROM_HERE, + base::MessageLoop::QuitClosure()); + test_phase_ = CLEAN_UP; + } else if (main_window_) { + main_window_->Close(); + } + main_window_ = NULL; + current_pid_ = base::kNullProcessId; +} + +void WebKitTestController::SendTestConfiguration() { + RenderViewHost* render_view_host = + main_window_->web_contents()->GetRenderViewHost(); + ShellTestConfiguration params; + params.current_working_directory = current_working_directory_; + params.temp_path = temp_path_; + params.test_url = test_url_; + params.enable_pixel_dumping = enable_pixel_dumping_; + params.allow_external_pages = CommandLine::ForCurrentProcess()->HasSwitch( + switches::kAllowExternalPages); + params.expected_pixel_hash = expected_pixel_hash_; + params.initial_size = initial_size_; + render_view_host->Send(new ShellViewMsg_SetTestConfiguration( + render_view_host->GetRoutingID(), params)); +} + +void WebKitTestController::OnTestFinished() { + test_phase_ = CLEAN_UP; + if (!printer_->output_finished()) + printer_->PrintImageFooter(); + RenderViewHost* render_view_host = + main_window_->web_contents()->GetRenderViewHost(); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(base::IgnoreResult(&WebKitTestController::Send), + base::Unretained(this), + new ShellViewMsg_Reset(render_view_host->GetRoutingID()))); +} + +void WebKitTestController::OnImageDump( + const std::string& actual_pixel_hash, + const SkBitmap& image) { + SkAutoLockPixels image_lock(image); + + printer_->PrintImageHeader(actual_pixel_hash, expected_pixel_hash_); + + // Only encode and dump the png if the hashes don't match. Encoding the + // image is really expensive. + if (actual_pixel_hash != expected_pixel_hash_) { + std::vector<unsigned char> png; + + // Only the expected PNGs for Mac have a valid alpha channel. +#if defined(OS_MACOSX) + bool discard_transparency = false; +#else + bool discard_transparency = true; +#endif + + std::vector<gfx::PNGCodec::Comment> comments; + comments.push_back(gfx::PNGCodec::Comment("checksum", actual_pixel_hash)); + bool success = gfx::PNGCodec::Encode( + static_cast<const unsigned char*>(image.getPixels()), + gfx::PNGCodec::FORMAT_BGRA, + gfx::Size(image.width(), image.height()), + static_cast<int>(image.rowBytes()), + discard_transparency, + comments, + &png); + if (success) + printer_->PrintImageBlock(png); + } + printer_->PrintImageFooter(); +} + +void WebKitTestController::OnAudioDump(const std::vector<unsigned char>& dump) { + printer_->PrintAudioHeader(); + printer_->PrintAudioBlock(dump); + printer_->PrintAudioFooter(); +} + +void WebKitTestController::OnTextDump(const std::string& dump) { + printer_->PrintTextHeader(); + printer_->PrintTextBlock(dump); + printer_->PrintTextFooter(); +} + +void WebKitTestController::OnPrintMessage(const std::string& message) { + printer_->AddMessageRaw(message); +} + +void WebKitTestController::OnOverridePreferences(const WebPreferences& prefs) { + should_override_prefs_ = true; + prefs_ = prefs; +} + +void WebKitTestController::OnShowDevTools() { + main_window_->ShowDevTools(); +} + +void WebKitTestController::OnCloseDevTools() { + main_window_->CloseDevTools(); +} + +void WebKitTestController::OnGoToOffset(int offset) { + main_window_->GoBackOrForward(offset); +} + +void WebKitTestController::OnReload() { + main_window_->Reload(); +} + +void WebKitTestController::OnLoadURLForFrame(const GURL& url, + const std::string& frame_name) { + main_window_->LoadURLForFrame(url, frame_name); +} + +void WebKitTestController::OnCaptureSessionHistory() { + std::vector<int> routing_ids; + std::vector<std::vector<PageState> > session_histories; + std::vector<unsigned> current_entry_indexes; + + RenderViewHost* render_view_host = + main_window_->web_contents()->GetRenderViewHost(); + + for (std::vector<Shell*>::iterator window = Shell::windows().begin(); + window != Shell::windows().end(); + ++window) { + WebContents* web_contents = (*window)->web_contents(); + // Only capture the history from windows in the same process as the main + // window. During layout tests, we only use two processes when an + // devtools window is open. This should not happen during history navigation + // tests. + if (render_view_host->GetProcess() != + web_contents->GetRenderViewHost()->GetProcess()) { + NOTREACHED(); + continue; + } + routing_ids.push_back(web_contents->GetRenderViewHost()->GetRoutingID()); + current_entry_indexes.push_back( + web_contents->GetController().GetCurrentEntryIndex()); + std::vector<PageState> history; + for (int entry = 0; entry < web_contents->GetController().GetEntryCount(); + ++entry) { + PageState state = web_contents->GetController().GetEntryAtIndex(entry)-> + GetPageState(); + if (!state.IsValid()) { + state = PageState::CreateFromURL( + web_contents->GetController().GetEntryAtIndex(entry)->GetURL()); + } + history.push_back(state); + } + session_histories.push_back(history); + } + + Send(new ShellViewMsg_SessionHistory(render_view_host->GetRoutingID(), + routing_ids, + session_histories, + current_entry_indexes)); +} + +void WebKitTestController::OnCloseRemainingWindows() { + DevToolsManager::GetInstance()->CloseAllClientHosts(); + std::vector<Shell*> open_windows(Shell::windows()); + for (size_t i = 0; i < open_windows.size(); ++i) { + if (open_windows[i] != main_window_) + open_windows[i]->Close(); + } + base::MessageLoop::current()->RunUntilIdle(); +} + +void WebKitTestController::OnResetDone() { + base::MessageLoop::current()->PostTask(FROM_HERE, + base::MessageLoop::QuitClosure()); +} + +} // namespace content diff --git a/content/shell/browser/webkit_test_controller.h b/content/shell/browser/webkit_test_controller.h new file mode 100644 index 0000000..58d1fa9 --- /dev/null +++ b/content/shell/browser/webkit_test_controller.h @@ -0,0 +1,219 @@ +// 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. + +#ifndef CONTENT_SHELL_BROWSER_WEBKIT_TEST_CONTROLLER_H_ +#define CONTENT_SHELL_BROWSER_WEBKIT_TEST_CONTROLLER_H_ + +#include <ostream> +#include <string> + +#include "base/cancelable_callback.h" +#include "base/files/file_path.h" +#include "base/synchronization/lock.h" +#include "base/threading/non_thread_safe.h" +#include "content/public/browser/gpu_data_manager_observer.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/render_view_host_observer.h" +#include "content/public/browser/web_contents_observer.h" +#include "ui/gfx/size.h" +#include "webkit/common/webpreferences.h" + +#if defined(OS_ANDROID) +#include "base/threading/thread_restrictions.h" +#endif + +class SkBitmap; + +namespace content { + +class Shell; + +#if defined(OS_ANDROID) +// Android uses a nested message loop for running layout tests because the +// default message loop, provided by the system, does not offer a blocking +// Run() method. The loop itself, implemented as NestedMessagePumpAndroid, +// uses a base::WaitableEvent allowing it to sleep until more events arrive. +class ScopedAllowWaitForAndroidLayoutTests { + private: + base::ThreadRestrictions::ScopedAllowWait wait; +}; +#endif + +class WebKitTestResultPrinter { + public: + WebKitTestResultPrinter(std::ostream* output, std::ostream* error); + ~WebKitTestResultPrinter(); + + void reset() { + state_ = DURING_TEST; + } + bool output_finished() const { return state_ == AFTER_TEST; } + void set_capture_text_only(bool capture_text_only) { + capture_text_only_ = capture_text_only; + } + + void set_encode_binary_data(bool encode_binary_data) { + encode_binary_data_ = encode_binary_data; + } + + void PrintTextHeader(); + void PrintTextBlock(const std::string& block); + void PrintTextFooter(); + + void PrintImageHeader(const std::string& actual_hash, + const std::string& expected_hash); + void PrintImageBlock(const std::vector<unsigned char>& png_image); + void PrintImageFooter(); + + void PrintAudioHeader(); + void PrintAudioBlock(const std::vector<unsigned char>& audio_data); + void PrintAudioFooter(); + + void AddMessage(const std::string& message); + void AddMessageRaw(const std::string& message); + void AddErrorMessage(const std::string& message); + + private: + void PrintEncodedBinaryData(const std::vector<unsigned char>& data); + + enum State { + DURING_TEST, + IN_TEXT_BLOCK, + IN_AUDIO_BLOCK, + IN_IMAGE_BLOCK, + AFTER_TEST + }; + State state_; + + bool capture_text_only_; + bool encode_binary_data_; + + std::ostream* output_; + std::ostream* error_; + + DISALLOW_COPY_AND_ASSIGN(WebKitTestResultPrinter); +}; + +class WebKitTestController : public base::NonThreadSafe, + public WebContentsObserver, + public NotificationObserver, + public GpuDataManagerObserver { + public: + static WebKitTestController* Get(); + + WebKitTestController(); + virtual ~WebKitTestController(); + + // True if the controller is ready for testing. + bool PrepareForLayoutTest(const GURL& test_url, + const base::FilePath& current_working_directory, + bool enable_pixel_dumping, + const std::string& expected_pixel_hash); + // True if the controller was reset successfully. + bool ResetAfterLayoutTest(); + + void SetTempPath(const base::FilePath& temp_path); + void RendererUnresponsive(); + void WorkerCrashed(); + void OverrideWebkitPrefs(WebPreferences* prefs); + void OpenURL(const GURL& url); + void TestFinishedInSecondaryWindow(); + bool IsMainWindow(WebContents* web_contents) const; + + WebKitTestResultPrinter* printer() { return printer_.get(); } + void set_printer(WebKitTestResultPrinter* printer) { + printer_.reset(printer); + } + + // WebContentsObserver implementation. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + virtual void PluginCrashed(const base::FilePath& plugin_path, + base::ProcessId plugin_pid) OVERRIDE; + virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE; + virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; + virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE; + + // NotificationObserver implementation. + virtual void Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) OVERRIDE; + + // GpuDataManagerObserver implementation. + virtual void OnGpuProcessCrashed(base::TerminationStatus exit_code) OVERRIDE; + + private: + enum TestPhase { + BETWEEN_TESTS, + DURING_TEST, + CLEAN_UP + }; + + static WebKitTestController* instance_; + + void TimeoutHandler(); + void DiscardMainWindow(); + void SendTestConfiguration(); + + // Message handlers. + void OnAudioDump(const std::vector<unsigned char>& audio_dump); + void OnImageDump(const std::string& actual_pixel_hash, const SkBitmap& image); + void OnTextDump(const std::string& dump); + void OnPrintMessage(const std::string& message); + void OnOverridePreferences(const WebPreferences& prefs); + void OnTestFinished(); + void OnShowDevTools(); + void OnCloseDevTools(); + void OnGoToOffset(int offset); + void OnReload(); + void OnLoadURLForFrame(const GURL& url, const std::string& frame_name); + void OnCaptureSessionHistory(); + void OnCloseRemainingWindows(); + void OnResetDone(); + + scoped_ptr<WebKitTestResultPrinter> printer_; + + base::FilePath current_working_directory_; + base::FilePath temp_path_; + + Shell* main_window_; + + // The PID of the render process of the render view host of main_window_. + int current_pid_; + + // True if we should set the test configuration to the next RenderViewHost + // created. + bool send_configuration_to_next_host_; + + // What phase of running an individual test we are currently in. + TestPhase test_phase_; + + // True if the currently running test is a compositing test. + bool is_compositing_test_; + + // Per test config. + bool enable_pixel_dumping_; + std::string expected_pixel_hash_; + gfx::Size initial_size_; + GURL test_url_; + + // True if the WebPreferences of newly created RenderViewHost should be + // overridden with prefs_. + bool should_override_prefs_; + WebPreferences prefs_; + + NotificationRegistrar registrar_; + +#if defined(OS_ANDROID) + // Because of the nested message pump implementation, Android needs to allow + // waiting on the UI thread while layout tests are being ran. + ScopedAllowWaitForAndroidLayoutTests reduced_restrictions_; +#endif + + DISALLOW_COPY_AND_ASSIGN(WebKitTestController); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_WEBKIT_TEST_CONTROLLER_H_ |