From faf19bbf27a42842d4d2bc9a1e689dfcac3bd675 Mon Sep 17 00:00:00 2001
From: "amanda@chromium.org"
 <amanda@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>
Date: Mon, 15 Sep 2008 19:54:42 +0000
Subject: Add test shell WebView and WebWidget host classes.  This are stopgaps
 and are subject to change as the rest of the rendering and event handling
 paths come together. Review URL: http://codereview.chromium.org/3062

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@2230 0039d316-1c4b-4281-b951-d872f2087c98
---
 webkit/tools/test_shell/mac/Info.plist             |   2 +-
 .../mac/TestShell.xcodeproj/project.pbxproj        |   6 +-
 .../tools/test_shell/mac/test_webview_delegate.mm  | 838 +++++++++++++++++++++
 webkit/tools/test_shell/mac/webview_host.mm        |  42 ++
 webkit/tools/test_shell/mac/webwidget_host.mm      | 260 +++++++
 5 files changed, 1144 insertions(+), 4 deletions(-)
 create mode 100644 webkit/tools/test_shell/mac/test_webview_delegate.mm
 create mode 100644 webkit/tools/test_shell/mac/webview_host.mm
 create mode 100644 webkit/tools/test_shell/mac/webwidget_host.mm

(limited to 'webkit/tools/test_shell')

diff --git a/webkit/tools/test_shell/mac/Info.plist b/webkit/tools/test_shell/mac/Info.plist
index d9a2cdb..7747ba0 100644
--- a/webkit/tools/test_shell/mac/Info.plist
+++ b/webkit/tools/test_shell/mac/Info.plist
@@ -9,7 +9,7 @@
 	<key>CFBundleIconFile</key>
 	<string></string>
 	<key>CFBundleIdentifier</key>
-	<string>com.yourcompany.TestShell</string>
+	<string>com.google.TestShell</string>
 	<key>CFBundleInfoDictionaryVersion</key>
 	<string>6.0</string>
 	<key>CFBundleName</key>
diff --git a/webkit/tools/test_shell/mac/TestShell.xcodeproj/project.pbxproj b/webkit/tools/test_shell/mac/TestShell.xcodeproj/project.pbxproj
index e72b12d..b5f988b 100644
--- a/webkit/tools/test_shell/mac/TestShell.xcodeproj/project.pbxproj
+++ b/webkit/tools/test_shell/mac/TestShell.xcodeproj/project.pbxproj
@@ -347,11 +347,11 @@
 		AB8A78520DC553A8005C27B8 /* event_sending_controller.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = event_sending_controller.cc; sourceTree = "<group>"; };
 		AB8A78540DC553BC005C27B8 /* node_leak_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_leak_test.cc; sourceTree = "<group>"; };
 		AB8A78560DC553C7005C27B8 /* test_shell_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test_shell_test.cc; sourceTree = "<group>"; };
-		AB8A78580DC553D7005C27B8 /* test_webview_delegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = test_webview_delegate.mm; sourceTree = "<group>"; };
+		AB8A78580DC553D7005C27B8 /* test_webview_delegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = test_webview_delegate.mm; path = mac/test_webview_delegate.mm; sourceTree = "<group>"; };
 		AB8A78590DC553D7005C27B8 /* test_webview_delegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = test_webview_delegate.h; sourceTree = "<group>"; };
 		AB8A785B0DC553E4005C27B8 /* text_input_controller.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = text_input_controller.cc; sourceTree = "<group>"; };
 		AB8A785C0DC553E4005C27B8 /* text_input_controller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = text_input_controller.h; sourceTree = "<group>"; };
-		AB8A785E0DC553EE005C27B8 /* webview_host.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = webview_host.mm; sourceTree = "<group>"; };
+		AB8A785E0DC553EE005C27B8 /* webview_host.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = webview_host.mm; path = mac/webview_host.mm; sourceTree = "<group>"; };
 		AB8A78600DC553EE005C27B8 /* webview_host.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = webview_host.h; sourceTree = "<group>"; };
 		AB8A786E0DC5544D005C27B8 /* test_shell_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = test_shell_test.h; sourceTree = "<group>"; };
 		ABA65EFD0DD50BFF003A4FC8 /* test_shell_webview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = test_shell_webview.h; sourceTree = "<group>"; };
@@ -362,7 +362,7 @@
 		ABC8C4180DC7F7D000B59C21 /* zlib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = zlib.xcodeproj; path = third_party/zlib/zlib.xcodeproj; sourceTree = "<group>"; };
 		ABCF253B0DB8436B00099567 /* test_shell_switches.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = test_shell_switches.h; sourceTree = "<group>"; };
 		ABCF253C0DB8436B00099567 /* test_shell_switches.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test_shell_switches.cc; sourceTree = "<group>"; };
-		ABD16F380DC6A73B0013D3AA /* webwidget_host.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = webwidget_host.mm; sourceTree = "<group>"; };
+		ABD16F380DC6A73B0013D3AA /* webwidget_host.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = webwidget_host.mm; path = mac/webwidget_host.mm; sourceTree = "<group>"; };
 		ABD16F390DC6A73B0013D3AA /* webwidget_host.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = webwidget_host.h; sourceTree = "<group>"; };
 		ABD16F700DC6CBDF0013D3AA /* base.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = base.xcodeproj; path = base/base.xcodeproj; sourceTree = "<group>"; };
 		E450637C0E4100740025A81A /* test_shell_request_context.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = test_shell_request_context.h; sourceTree = "<group>"; };
diff --git a/webkit/tools/test_shell/mac/test_webview_delegate.mm b/webkit/tools/test_shell/mac/test_webview_delegate.mm
new file mode 100644
index 0000000..746835e
--- /dev/null
+++ b/webkit/tools/test_shell/mac/test_webview_delegate.mm
@@ -0,0 +1,838 @@
+// Copyright (c) 2008 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 "webkit/tools/test_shell/test_webview_delegate.h"
+
+#import <Cocoa/Cocoa.h>
+#include "base/gfx/point.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+#include "chrome/common/page_transition_types.h"
+#include "webkit/glue/webdatasource.h"
+#include "webkit/glue/webdropdata.h"
+#include "webkit/glue/weberror.h"
+#include "webkit/glue/webframe.h"
+#include "webkit/glue/webpreferences.h"
+#include "webkit/glue/weburlrequest.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/webview.h"
+// #include "webkit/glue/plugins/plugin_list.h"
+// #include "webkit/glue/plugins/webplugin_delegate_impl.h"
+#include "webkit/glue/window_open_disposition.h"
+// #include "webkit/tools/test_shell/drag_delegate.h"
+// #include "webkit/tools/test_shell/drop_delegate.h"
+#include "webkit/tools/test_shell/test_navigation_controller.h"
+#include "webkit/tools/test_shell/test_shell.h"
+
+
+static int32 next_page_id_ = 1;
+
+// WebViewDelegate -----------------------------------------------------------
+
+TestWebViewDelegate::~TestWebViewDelegate() {
+}
+
+WebView* TestWebViewDelegate::CreateWebView(WebView* webview,
+                                            bool user_gesture) {
+  return shell_->CreateWebView(webview);
+}
+
+WebWidget* TestWebViewDelegate::CreatePopupWidget(WebView* webview) {
+  return shell_->CreatePopupWidget(webview);
+}
+
+WebPluginDelegate* TestWebViewDelegate::CreatePluginDelegate(
+    WebView* webview,
+    const GURL& url,
+    const std::string& mime_type,
+    const std::string& clsid,
+    std::string* actual_mime_type) {
+  // TODO(awalker): once Mac plugins are working, enable this code to
+  // connect up the web plugin delegate for a plugin.
+#if 0
+  WindowHandle WindowHandle = GetContainingWindow(webview);
+  if (!WindowHandle)
+    return NULL;
+
+  bool allow_wildcard = true;
+  WebPluginInfo info;
+  if (!NPAPI::PluginList::Singleton()->GetPluginInfo(url, mime_type, clsid,
+                                                     allow_wildcard, &info,
+                                                     actual_mime_type))
+    return NULL;
+
+  if (actual_mime_type && !actual_mime_type->empty())
+    return WebPluginDelegateImpl::Create(info.file, *actual_mime_type,
+                                         WindowHandle);
+  else
+    return WebPluginDelegateImpl::Create(info.file, mime_type, WindowHandle);
+#else
+  return NULL;
+#endif
+}
+
+void TestWebViewDelegate::OpenURL(WebView* webview, const GURL& url,
+                                  WindowOpenDisposition disposition) {
+  DCHECK_NE(disposition, CURRENT_TAB);  // No code for this
+  if (disposition == SUPPRESS_OPEN)
+    return;
+  TestShell* shell = NULL;
+  if (TestShell::CreateNewWindow(UTF8ToWide(url.spec()), &shell))
+    shell->Show(shell->webView(), disposition);
+}
+
+void TestWebViewDelegate::DidStartLoading(WebView* webview) {
+  if (page_is_loading_) {
+    LOG(ERROR) << "DidStartLoading called while loading";
+    return;
+  }
+  page_is_loading_ = true;
+}
+
+void TestWebViewDelegate::DidStopLoading(WebView* webview) {
+  if (!page_is_loading_) {
+    LOG(ERROR) << "DidStopLoading called while not loading";
+    return;
+  }
+  page_is_loading_ = false;
+}
+
+void TestWebViewDelegate::WindowObjectCleared(WebFrame* webframe) {
+  shell_->BindJSObjectsToWindow(webframe);
+}
+
+WindowOpenDisposition TestWebViewDelegate::DispositionForNavigationAction(
+    WebView* webview,
+    WebFrame* frame,
+    const WebRequest* request,
+    WebNavigationType type,
+    WindowOpenDisposition disposition,
+    bool is_redirect) {
+  if (is_custom_policy_delegate_) {
+    std::wstring frame_name = frame->GetName();
+    printf("Policy delegate: attempt to load %s\n",
+           request->GetURL().spec().c_str());
+    return IGNORE_ACTION;
+  } else {
+    return static_cast<WindowOpenDisposition>(
+        WebViewDelegate::DispositionForNavigationAction(webview, frame, request,
+                                                        type, disposition,
+                                                        is_redirect));
+  }
+}
+
+void TestWebViewDelegate::SetCustomPolicyDelegate(bool isCustom) {
+  is_custom_policy_delegate_ = isCustom;
+}
+
+void TestWebViewDelegate::AssignIdentifierToRequest(WebView* webview,
+                                                    uint32 identifier,
+                                                    const WebRequest& request) {
+  if (shell_->ShouldDumpResourceLoadCallbacks()) {
+    resource_identifier_map_[identifier] = request.GetURL().spec();
+  }
+}
+
+std::string TestWebViewDelegate::GetResourceDescription(uint32 identifier) {
+  ResourceMap::iterator it = resource_identifier_map_.find(identifier);
+  return it != resource_identifier_map_.end() ? it->second : "<unknown>";
+}
+
+void TestWebViewDelegate::WillSendRequest(WebView* webview,
+                                          uint32 identifier,
+                                          WebRequest* request) {
+  std::string request_url = request->GetURL().spec();
+
+  if (shell_->ShouldDumpResourceLoadCallbacks()) {
+    printf("%s - willSendRequest <WebRequest URL \"%s\">\n",
+           GetResourceDescription(identifier).c_str(),
+           request_url.c_str());
+  }
+
+  // Set the new substituted URL.
+  request->SetURL(GURL(TestShell::RewriteLocalUrl(request_url)));
+}
+
+void TestWebViewDelegate::DidFinishLoading(WebView* webview,
+                                           uint32 identifier) {
+  if (shell_->ShouldDumpResourceLoadCallbacks()) {
+    printf("%s - didFinishLoading\n",
+           GetResourceDescription(identifier).c_str());
+  }
+
+  resource_identifier_map_.erase(identifier);
+}
+
+void TestWebViewDelegate::DidFailLoadingWithError(WebView* webview,
+                                                  uint32 identifier,
+                                                  const WebError& error) {
+  if (shell_->ShouldDumpResourceLoadCallbacks()) {
+    printf("%s - didFailLoadingWithError <WebError code %d,"
+           " failing URL \"%s\">\n",
+           GetResourceDescription(identifier).c_str(),
+           error.GetErrorCode(),
+           error.GetFailedURL().spec().c_str());
+  }
+
+  resource_identifier_map_.erase(identifier);
+}
+
+void TestWebViewDelegate::DidStartProvisionalLoadForFrame(
+    WebView* webview,
+    WebFrame* frame,
+    NavigationGesture gesture) {
+  if (shell_->ShouldDumpFrameLoadCallbacks()) {
+    printf("%S - didStartProvisionalLoadForFrame\n",
+           GetFrameDescription(frame).c_str());
+  }
+
+  if (!top_loading_frame_) {
+    top_loading_frame_ = frame;
+  }
+  UpdateAddressBar(webview);
+}
+
+void TestWebViewDelegate::DidReceiveServerRedirectForProvisionalLoadForFrame(
+    WebView* webview,
+    WebFrame* frame) {
+  if (shell_->ShouldDumpFrameLoadCallbacks()) {
+    printf("%S - didReceiveServerRedirectForProvisionalLoadForFrame\n",
+           GetFrameDescription(frame).c_str());
+  }
+
+  UpdateAddressBar(webview);
+}
+
+void TestWebViewDelegate::DidFailProvisionalLoadWithError(
+    WebView* webview,
+    const WebError& error,
+    WebFrame* frame) {
+  if (shell_->ShouldDumpFrameLoadCallbacks()) {
+    printf("%S - didFailProvisionalLoadWithError\n",
+           GetFrameDescription(frame).c_str());
+  }
+
+  if (page_is_loading_)
+    DidStopLoading(webview);
+  LocationChangeDone(frame->GetProvisionalDataSource());
+
+  // Don't display an error page if we're running layout tests, because
+  // DumpRenderTree doesn't.
+  if (!shell_->interactive())
+    return;
+
+  // Don't display an error page if this is simply a cancelled load.  Aside
+  // from being dumb, WebCore doesn't expect it and it will cause a crash.
+  if (error.GetErrorCode() == net::ERR_ABORTED)
+    return;
+
+  const WebRequest& failed_request =
+      frame->GetProvisionalDataSource()->GetRequest();
+  TestShellExtraRequestData* extra_data =
+      static_cast<TestShellExtraRequestData*>(failed_request.GetExtraData());
+  bool replace = extra_data && extra_data->pending_page_id != -1;
+
+  scoped_ptr<WebRequest> request(failed_request.Clone());
+  request->SetURL(GURL("testshell-error:"));
+
+  std::string error_text =
+      StringPrintf("Error loading url: %d", error.GetErrorCode());
+
+  frame->LoadAlternateHTMLString(request.get(), error_text,
+                                 error.GetFailedURL(), replace);
+}
+
+void TestWebViewDelegate::DidCommitLoadForFrame(WebView* webview,
+                                                WebFrame* frame,
+                                                bool is_new_navigation) {
+  if (shell_->ShouldDumpFrameLoadCallbacks()) {
+    printf("%S - didCommitLoadForFrame\n",
+           GetFrameDescription(frame).c_str());
+  }
+
+  UpdateForCommittedLoad(frame, is_new_navigation);
+}
+
+void TestWebViewDelegate::DidReceiveTitle(WebView* webview,
+                                          const std::wstring& title,
+                                          WebFrame* frame) {
+  if (shell_->ShouldDumpFrameLoadCallbacks()) {
+    printf("%S - didReceiveTitle\n",
+           GetFrameDescription(frame).c_str());
+  }
+
+  if (shell_->ShouldDumpTitleChanges()) {
+    printf("TITLE CHANGED: %S\n", title.c_str());
+  }
+  [[shell_->webViewHost()->window_handle() window]
+      setTitle:[NSString stringWithUTF8String:WideToUTF8(title).c_str()]];
+}
+
+void TestWebViewDelegate::DidFinishLoadForFrame(WebView* webview,
+                                                WebFrame* frame) {
+  if (shell_->ShouldDumpFrameLoadCallbacks()) {
+    printf("%S - didFinishLoadForFrame\n",
+           GetFrameDescription(frame).c_str());
+  }
+
+  UpdateAddressBar(webview);
+  LocationChangeDone(frame->GetDataSource());
+}
+
+void TestWebViewDelegate::DidFailLoadWithError(WebView* webview,
+                                               const WebError& error,
+                                               WebFrame* frame) {
+  if (shell_->ShouldDumpFrameLoadCallbacks()) {
+    printf("%S - didFailLoadWithError\n",
+           GetFrameDescription(frame).c_str());
+  }
+
+  if (page_is_loading_)
+    DidStopLoading(webview);
+  LocationChangeDone(frame->GetDataSource());
+}
+
+void TestWebViewDelegate::DidFinishDocumentLoadForFrame(WebView* webview,
+                                                        WebFrame* frame) {
+  if (shell_->ShouldDumpFrameLoadCallbacks()) {
+    printf("%S - didFinishDocumentLoadForFrame\n",
+           GetFrameDescription(frame).c_str());
+  }
+}
+
+void TestWebViewDelegate::DidHandleOnloadEventsForFrame(WebView* webview,
+                                                        WebFrame* frame) {
+  if (shell_->ShouldDumpFrameLoadCallbacks()) {
+    printf("%S - didHandleOnloadEventsForFrame\n",
+           GetFrameDescription(frame).c_str());
+  }
+}
+
+void TestWebViewDelegate::DidChangeLocationWithinPageForFrame(
+    WebView* webview, WebFrame* frame, bool is_new_navigation) {
+  if (shell_->ShouldDumpFrameLoadCallbacks()) {
+    printf("%S - didChangeLocationWithinPageForFrame\n",
+           GetFrameDescription(frame).c_str());
+  }
+
+  UpdateForCommittedLoad(frame, is_new_navigation);
+}
+
+void TestWebViewDelegate::DidReceiveIconForFrame(WebView* webview,
+                                                 WebFrame* frame) {
+  if (shell_->ShouldDumpFrameLoadCallbacks()) {
+    printf("%S - didReceiveIconForFrame\n",
+           GetFrameDescription(frame).c_str());
+  }
+}
+
+void TestWebViewDelegate::WillPerformClientRedirect(
+    WebView* webview, WebFrame* frame, const std::wstring& dest_url,
+    unsigned int delay_seconds, unsigned int fire_date) {
+  if (shell_->ShouldDumpFrameLoadCallbacks()) {
+    // TODO: prettyprint the url?
+    printf("%S - willPerformClientRedirectToURL: %S\n",
+           GetFrameDescription(frame).c_str(), dest_url.c_str());
+  }
+}
+
+void TestWebViewDelegate::DidCancelClientRedirect(WebView* webview,
+                                                  WebFrame* frame) {
+  if (shell_->ShouldDumpFrameLoadCallbacks()) {
+    printf("%S - didCancelClientRedirectForFrame\n",
+           GetFrameDescription(frame).c_str());
+  }
+}
+
+void TestWebViewDelegate::AddMessageToConsole(WebView* webview,
+                                              const std::wstring& message,
+                                              unsigned int line_no,
+                                              const std::wstring& source_id) {
+  if (shell_->interactive()) {
+    logging::LogMessage("CONSOLE", 0).stream() << "\"" 
+                                               << message.c_str() 
+                                               << ",\" source: " 
+                                               << source_id.c_str() 
+                                               << "(" 
+                                               << line_no 
+                                               << ")";
+  } else {
+    // This matches win DumpRenderTree's UIDelegate.cpp.
+    std::wstring new_message = message;
+    if (!message.empty()) {
+      new_message = message;
+      size_t file_protocol = new_message.find(L"file://");
+      if (file_protocol != std::wstring::npos) {
+        new_message = new_message.substr(0, file_protocol) + new_message;
+            // UrlSuitableForTestResult(new_message);
+      }
+    }
+
+    std::string utf8 = WideToUTF8(new_message);
+    printf("CONSOLE MESSAGE: line %d: %s\n", line_no, utf8.c_str());
+  }
+}
+
+void TestWebViewDelegate::RunJavaScriptAlert(WebView* webview,
+                                             const std::wstring& message) {
+  if (shell_->interactive()) {
+    NSString *text =
+        [NSString stringWithUTF8String:WideToUTF8(message).c_str()];
+    NSAlert *alert = [NSAlert alertWithMessageText:@"JavaScript Alert"
+                                     defaultButton:@"OK"
+                                   alternateButton:nil
+                                       otherButton:nil
+                         informativeTextWithFormat:text];
+    [alert runModal];
+  } else {
+    std::string utf8 = WideToUTF8(message);
+    printf("ALERT: %s\n", utf8.c_str());
+  }
+}
+
+bool TestWebViewDelegate::RunJavaScriptConfirm(WebView* webview,
+                                               const std::wstring& message) {
+  if (!shell_->interactive()) {
+    // When running tests, write to stdout.
+    std::string utf8 = WideToUTF8(message);
+    printf("CONFIRM: %s\n", utf8.c_str());
+    return true;
+  }
+  return false;
+}
+
+bool TestWebViewDelegate::RunJavaScriptPrompt(WebView* webview,
+    const std::wstring& message, const std::wstring& default_value,
+    std::wstring* result) {
+  if (!shell_->interactive()) {
+    // When running tests, write to stdout.
+    std::string utf8_message = WideToUTF8(message);
+    std::string utf8_default_value = WideToUTF8(default_value);
+    printf("PROMPT: %s, default text: %s\n", utf8_message.c_str(),
+           utf8_default_value.c_str());
+    return true;
+  }
+  return false;
+}
+
+void TestWebViewDelegate::StartDragging(WebView* webview,
+                                        const WebDropData& drop_data) {
+#if 0
+// TODO(pinkerton): fill in drag&drop code later.
+  if (!drag_delegate_)
+    drag_delegate_ = new TestDragDelegate(shell_->webViewWnd(),
+                                          shell_->webView());
+  if (webkit_glue::IsLayoutTestMode()) {
+    if (shell_->layout_test_controller()->ShouldAddFileToPasteboard()) {
+      // Add a file called DRTFakeFile to the drag&drop clipboard.
+      // AddDRTFakeFileToDataObject(drop_data.data_object);
+    }
+
+    // When running a test, we need to fake a drag drop operation otherwise
+    // Windows waits for real mouse events to know when the drag is over.
+    EventSendingController::DoDragDrop(drop_data.data_object);
+  } else {
+    // Process a real drag and drop
+  }
+  webview->DragSourceSystemDragEnded();
+#endif
+}
+
+void TestWebViewDelegate::ShowContextMenu(WebView* webview,
+                                          ContextNode::Type type,
+                                          int x,
+                                          int y,
+                                          const GURL& link_url,
+                                          const GURL& image_url,
+                                          const GURL& page_url,
+                                          const GURL& frame_url,
+                                          const std::wstring& selection_text,
+                                          const std::wstring& misspelled_word,
+                                          int edit_flags) {
+  CapturedContextMenuEvent context(type, x, y);
+  captured_context_menu_events_.push_back(context);
+}
+
+// The output from these methods in non-interactive mode should match that
+// expected by the layout tests.  See EditingDelegate.m in DumpRenderTree.
+bool TestWebViewDelegate::ShouldBeginEditing(WebView* webview, 
+                                             std::wstring range) {
+  if (shell_->ShouldDumpEditingCallbacks()) {
+    std::string utf8 = WideToUTF8(range);
+    printf("EDITING DELEGATE: shouldBeginEditingInDOMRange:%s\n", 
+           utf8.c_str());
+  }
+  return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldEndEditing(WebView* webview, 
+                                           std::wstring range) {
+  if (shell_->ShouldDumpEditingCallbacks()) {
+    std::string utf8 = WideToUTF8(range);
+    printf("EDITING DELEGATE: shouldEndEditingInDOMRange:%s\n", 
+           utf8.c_str());
+  }
+  return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldInsertNode(WebView* webview, 
+                                           std::wstring node, 
+                                           std::wstring range,
+                                           std::wstring action) {
+  if (shell_->ShouldDumpEditingCallbacks()) {
+    std::string utf8_node = WideToUTF8(node);
+    std::string utf8_range = WideToUTF8(range);
+    std::string utf8_action = WideToUTF8(action);
+    printf("EDITING DELEGATE: shouldInsertNode:%s "
+               "replacingDOMRange:%s givenAction:%s\n", 
+           utf8_node.c_str(), utf8_range.c_str(), utf8_action.c_str());
+  }
+  return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldInsertText(WebView* webview, 
+                                           std::wstring text, 
+                                           std::wstring range,
+                                           std::wstring action) {
+  if (shell_->ShouldDumpEditingCallbacks()) {
+    std::string utf8_text = WideToUTF8(text);
+    std::string utf8_range = WideToUTF8(range);
+    std::string utf8_action = WideToUTF8(action);
+    printf("EDITING DELEGATE: shouldInsertText:%s "
+               "replacingDOMRange:%s givenAction:%s\n", 
+           utf8_text.c_str(), utf8_range.c_str(), utf8_action.c_str());
+  }
+  return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldChangeSelectedRange(WebView* webview, 
+                                                    std::wstring fromRange, 
+                                                    std::wstring toRange, 
+                                                    std::wstring affinity, 
+                                                    bool stillSelecting) {
+  if (shell_->ShouldDumpEditingCallbacks()) {
+    std::string utf8_from = WideToUTF8(fromRange);
+    std::string utf8_to = WideToUTF8(toRange);
+    std::string utf8_affinity = WideToUTF8(affinity);
+    printf("EDITING DELEGATE: shouldChangeSelectedDOMRange:%s "
+               "toDOMRange:%s affinity:%s stillSelecting:%s\n", 
+           utf8_from.c_str(), 
+           utf8_to.c_str(), 
+           utf8_affinity.c_str(),
+           (stillSelecting ? "TRUE" : "FALSE"));
+  }
+  return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldDeleteRange(WebView* webview, 
+                                            std::wstring range) {
+  if (shell_->ShouldDumpEditingCallbacks()) {
+    std::string utf8 = WideToUTF8(range);
+    printf("EDITING DELEGATE: shouldDeleteDOMRange:%s\n", utf8.c_str());
+  }
+  return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::ShouldApplyStyle(WebView* webview, 
+                                           std::wstring style,
+                                           std::wstring range) {
+  if (shell_->ShouldDumpEditingCallbacks()) {
+    std::string utf8_style = WideToUTF8(style);
+    std::string utf8_range = WideToUTF8(range);
+    printf("EDITING DELEGATE: shouldApplyStyle:%s toElementsInDOMRange:%s\n", 
+           utf8_style.c_str(), utf8_range.c_str());
+  }
+  return shell_->AcceptsEditing();
+}
+
+bool TestWebViewDelegate::SmartInsertDeleteEnabled() {
+  return true;
+}
+
+void TestWebViewDelegate::DidBeginEditing() { 
+  if (shell_->ShouldDumpEditingCallbacks()) {
+    printf("EDITING DELEGATE: "
+           "webViewDidBeginEditing:WebViewDidBeginEditingNotification\n");
+  }
+}
+
+void TestWebViewDelegate::DidChangeSelection() {
+  if (shell_->ShouldDumpEditingCallbacks()) {
+    printf("EDITING DELEGATE: "
+    "webViewDidChangeSelection:WebViewDidChangeSelectionNotification\n");
+  }
+}
+
+void TestWebViewDelegate::DidChangeContents() {
+  if (shell_->ShouldDumpEditingCallbacks()) {
+    printf("EDITING DELEGATE: "
+           "webViewDidChange:WebViewDidChangeNotification\n");
+  }
+}
+
+void TestWebViewDelegate::DidEndEditing() {
+  if (shell_->ShouldDumpEditingCallbacks()) {
+    printf("EDITING DELEGATE: "
+           "webViewDidEndEditing:WebViewDidEndEditingNotification\n");
+  }
+}
+
+WebHistoryItem* TestWebViewDelegate::GetHistoryEntryAtOffset(int offset) {
+  TestNavigationEntry* entry = static_cast<TestNavigationEntry*>(
+      shell_->navigation_controller()->GetEntryAtOffset(offset));
+  if (!entry)
+    return NULL;
+
+  return entry->GetHistoryItem();
+}
+
+void TestWebViewDelegate::GoToEntryAtOffsetAsync(int offset) {
+  shell_->navigation_controller()->GoToOffset(offset);
+}
+
+int TestWebViewDelegate::GetHistoryBackListCount() {
+  int current_index =
+      shell_->navigation_controller()->GetLastCommittedEntryIndex();
+  return current_index;
+}
+
+int TestWebViewDelegate::GetHistoryForwardListCount() {
+  int current_index =
+      shell_->navigation_controller()->GetLastCommittedEntryIndex();
+  return shell_->navigation_controller()->GetEntryCount() - current_index - 1;
+}
+
+void TestWebViewDelegate::SetUserStyleSheetEnabled(bool is_enabled) {
+  WebPreferences* prefs = shell_->GetWebPreferences();
+  prefs->user_style_sheet_enabled = is_enabled;
+  shell_->webView()->SetPreferences(*prefs);
+}
+
+void TestWebViewDelegate::SetUserStyleSheetLocation(const GURL& location) {
+  WebPreferences* prefs = shell_->GetWebPreferences();
+  prefs->user_style_sheet_location = location;
+  shell_->webView()->SetPreferences(*prefs);
+}
+
+void TestWebViewDelegate::SetDashboardCompatibilityMode(bool use_mode) {
+  WebPreferences* prefs = shell_->GetWebPreferences();
+  prefs->dashboard_compatibility_mode = use_mode;
+  shell_->webView()->SetPreferences(*prefs);
+}
+
+// WebWidgetDelegate ---------------------------------------------------------
+
+gfx::ViewHandle TestWebViewDelegate::GetContainingWindow(WebWidget* webwidget) {
+  if (WebWidgetHost* host = GetHostForWidget(webwidget))
+    return host->window_handle();
+
+  return NULL;
+}
+
+void TestWebViewDelegate::DidInvalidateRect(WebWidget* webwidget,
+                                            const gfx::Rect& rect) {
+  if (shell_ && shell_->webViewHost() && shell_->webViewHost()->window_handle())
+    [shell_->webViewHost()->window_handle() setNeedsDisplay:YES];
+}
+
+void TestWebViewDelegate::DidScrollRect(WebWidget* webwidget, int dx, int dy,
+                                        const gfx::Rect& clip_rect) {
+  if (WebWidgetHost* host = GetHostForWidget(webwidget))
+    host->DidScrollRect(dx, dy, clip_rect);
+}
+
+void TestWebViewDelegate::Show(WebWidget* webview,
+                               WindowOpenDisposition disposition) {
+}
+
+void TestWebViewDelegate::CloseWidgetSoon(WebWidget* webwidget) {
+  if (webwidget == shell_->webView()) {
+    NSWindow *win = shell_->mainWnd();
+    // Tell Cocoa to close the window.  When the view is deallocated,
+    // the delegate (and therefore shell_) will be destroyed as well, so
+    // we do not need to explicitly null out the pointer shell_ is
+    // holding to the window.
+    [win performClose:nil];
+  } else if (webwidget == shell_->popup()) {
+    shell_->ClosePopup();
+  }
+}
+
+void TestWebViewDelegate::Focus(WebWidget* webwidget) {
+  if (WebWidgetHost* host = GetHostForWidget(webwidget))
+    shell_->SetFocus(host, true);
+}
+
+void TestWebViewDelegate::Blur(WebWidget* webwidget) {
+  if (WebWidgetHost* host = GetHostForWidget(webwidget))
+    shell_->SetFocus(host, false);
+}
+
+void TestWebViewDelegate::SetCursor(WebWidget* webwidget, 
+                                    const WebCursor& cursor) {
+  //TODO: Mac cursor handling
+}
+
+void TestWebViewDelegate::GetWindowRect(WebWidget* webwidget,
+                                        gfx::Rect* out_rect) {
+  DCHECK(out_rect);
+  if (WebWidgetHost* host = GetHostForWidget(webwidget)) {
+    NSView *view = host->window_handle();
+    NSRect rect = [[[view window] contentView] frame];
+    *out_rect = gfx::Rect(NSRectToCGRect(rect));
+  }
+}
+
+void TestWebViewDelegate::SetWindowRect(WebWidget* webwidget,
+                                        const gfx::Rect& rect) {
+  // TODO: Mac window movement
+  if (webwidget == shell_->webView()) {
+    // ignored
+  } else if (webwidget == shell_->popup()) {
+    // MoveWindow(shell_->popupWnd(),
+    //            rect.x(), rect.y(), rect.width(), rect.height(), FALSE);
+  }
+}
+
+void TestWebViewDelegate::DidMove(WebWidget* webwidget,
+                                  const WebPluginGeometry& move) {
+  // TODO: uncomment when Mac plugins are working
+  // WebPluginDelegateImpl::MoveWindow(
+  //     move.window, move.window_rect, move.clip_rect, move.visible); */
+}
+
+void TestWebViewDelegate::RegisterDragDrop() {
+  // TODO: uncomment when Mac drag and drop is working
+  // DCHECK(!drop_delegate_);
+  // drop_delegate_ = new TestDropDelegate(shell_->webViewWnd(),
+  //                                        shell_->webView());
+}
+
+void TestWebViewDelegate::RunModal(WebWidget* webwidget) {
+  // TODO(pinkerton): implement me
+}
+
+// Private methods -----------------------------------------------------------
+
+void TestWebViewDelegate::UpdateAddressBar(WebView* webView) {
+  WebFrame* mainFrame = webView->GetMainFrame();
+
+  WebDataSource* dataSource = mainFrame->GetDataSource();
+  if (!dataSource)
+    dataSource = mainFrame->GetProvisionalDataSource();
+  if (!dataSource)
+    return;
+
+  std::string frameURL = dataSource->GetRequest().GetMainDocumentURL().spec();
+  NSString *address = [NSString stringWithUTF8String:frameURL.c_str()];
+  [shell_->editWnd() setStringValue:address];
+}
+
+void TestWebViewDelegate::LocationChangeDone(WebDataSource* data_source) {
+  if (data_source->GetWebFrame() == top_loading_frame_) {
+    top_loading_frame_ = NULL;
+    [shell_->webViewHost()->window_handle() setNeedsDisplay:YES];
+
+    if (!shell_->interactive())
+      shell_->layout_test_controller()->LocationChangeDone();
+  }
+}
+
+WebWidgetHost* TestWebViewDelegate::GetHostForWidget(WebWidget* webwidget) {
+  if (webwidget == shell_->webView())
+    return shell_->webViewHost();
+  if (webwidget == shell_->popup())
+    return shell_->popupHost();
+  return NULL;
+}
+
+void TestWebViewDelegate::UpdateForCommittedLoad(WebFrame* frame,
+                                                 bool is_new_navigation) {
+  WebView* webview = shell_->webView();
+
+  // Code duplicated from RenderView::DidCommitLoadForFrame.
+  const WebRequest& request =
+      webview->GetMainFrame()->GetDataSource()->GetRequest();
+  TestShellExtraRequestData* extra_data =
+      static_cast<TestShellExtraRequestData*>(request.GetExtraData());
+
+  if (is_new_navigation) {
+    // New navigation.
+    UpdateSessionHistory(frame);
+    page_id_ = next_page_id_++;
+  } else if (extra_data && extra_data->pending_page_id != -1 &&
+             !extra_data->request_committed) {
+    // This is a successful session history navigation!
+    UpdateSessionHistory(frame);
+    page_id_ = extra_data->pending_page_id;
+  }
+
+  // Don't update session history multiple times.
+  if (extra_data)
+    extra_data->request_committed = true;
+
+  UpdateURL(frame);
+}
+
+void TestWebViewDelegate::UpdateURL(WebFrame* frame) {
+  WebDataSource* ds = frame->GetDataSource();
+  DCHECK(ds);
+
+  const WebRequest& request = ds->GetRequest();
+
+  scoped_ptr<TestNavigationEntry> entry(new TestNavigationEntry);
+
+  // Bug 654101: the referrer will be empty on https->http transitions. It
+  // would be nice if we could get the real referrer from somewhere.
+  entry->SetPageID(page_id_);
+  if (ds->HasUnreachableURL()) {
+    entry->SetURL(GURL(ds->GetUnreachableURL()));
+  } else {
+    entry->SetURL(GURL(request.GetURL()));
+  }
+
+  shell_->navigation_controller()->DidNavigateToEntry(entry.release());
+
+  last_page_id_updated_ = std::max(last_page_id_updated_, page_id_);
+}
+
+void TestWebViewDelegate::UpdateSessionHistory(WebFrame* frame) {
+  // If we have a valid page ID at this point, then it corresponds to the page
+  // we are navigating away from.  Otherwise, this is the first navigation, so
+  // there is no past session history to record.
+  if (page_id_ == -1)
+    return;
+
+  TestNavigationEntry* entry = static_cast<TestNavigationEntry*>(
+      shell_->navigation_controller()->GetEntryWithPageID(page_id_));
+  if (!entry)
+    return;
+
+  GURL url;
+  std::wstring title;
+  std::string state;
+  if (!shell_->webView()->GetMainFrame()->
+      GetPreviousState(&url, &title, &state))
+    return;
+
+  entry->SetURL(url);
+  entry->SetTitle(title);
+  entry->SetContentState(state);
+}
+
+std::wstring TestWebViewDelegate::GetFrameDescription(WebFrame* webframe) {
+  std::wstring name = webframe->GetName();
+
+  if (webframe == shell_->webView()->GetMainFrame()) {
+    if (name.length())
+      return L"main frame \"" + name + L"\"";
+    else
+      return L"main frame";
+  } else {
+    if (name.length())
+      return L"frame \"" + name + L"\"";
+    else
+      return L"frame (anonymous)";
+  }
+}
diff --git a/webkit/tools/test_shell/mac/webview_host.mm b/webkit/tools/test_shell/mac/webview_host.mm
new file mode 100644
index 0000000..803555a
--- /dev/null
+++ b/webkit/tools/test_shell/mac/webview_host.mm
@@ -0,0 +1,42 @@
+// Copyright (c) 2008 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.
+
+#import <Cocoa/Cocoa.h>
+
+#include "webkit/tools/test_shell/webview_host.h"
+#include "webkit/tools/test_shell/mac/test_shell_webview.h"
+
+#include "base/gfx/platform_canvas.h"
+#include "base/gfx/rect.h"
+#include "base/gfx/size.h"
+#include "webkit/glue/webinputevent.h"
+#include "webkit/glue/webview.h"
+
+/*static*/
+WebViewHost* WebViewHost::Create(NSWindow *parent_window,
+                                 WebViewDelegate* delegate,
+                                 const WebPreferences& prefs) {
+  WebViewHost* host = new WebViewHost();
+
+  NSRect content_rect = [[parent_window contentView] frame];
+  // bump down the top of the view so that it doesn't overlap the buttons
+  // and URL field.  32 is an ad hoc constant.
+  // TODO(awalker): replace explicit view layout with a little nib file
+  // and use that for view geometry.
+  content_rect.size.height -= 32;
+  host->view_ = [[TestShellWebView alloc] initWithFrame:content_rect];
+  // make the height and width track the window size.
+  [host->view_ setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
+  [[parent_window contentView] addSubview:host->view_];
+
+  host->webwidget_ = WebView::Create(delegate, prefs);
+  host->webwidget_->Resize(gfx::Size(content_rect.size.width,
+                                     content_rect.size.height));
+
+  return host;
+}
+
+WebView* WebViewHost::webview() const {
+  return static_cast<WebView*>(webwidget_);
+}
diff --git a/webkit/tools/test_shell/mac/webwidget_host.mm b/webkit/tools/test_shell/mac/webwidget_host.mm
new file mode 100644
index 0000000..bf38fc4
--- /dev/null
+++ b/webkit/tools/test_shell/mac/webwidget_host.mm
@@ -0,0 +1,260 @@
+// Copyright (c) 2008 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.
+
+#import <Cocoa/Cocoa.h>
+
+#include "webkit/tools/test_shell/webwidget_host.h"
+
+#include "base/gfx/platform_canvas_mac.h"
+#include "base/gfx/rect.h"
+#include "base/gfx/size.h"
+#include "base/logging.h"
+#include "webkit/glue/webinputevent.h"
+#include "webkit/glue/webwidget.h"
+
+/*static*/
+WebWidgetHost* WebWidgetHost::Create(NSWindow* parent_window,
+                                     WebWidgetDelegate* delegate) {
+  WebWidgetHost* host = new WebWidgetHost();
+
+  NSRect content_rect = [parent_window frame];
+  content_rect.origin.y += 64;
+  content_rect.size.height -= 64;
+  host->view_ = [[NSView alloc] initWithFrame:content_rect];
+  [[parent_window contentView] addSubview:host->view_];
+  
+  // win_util::SetWindowUserData(host->hwnd_, host);
+
+  host->webwidget_ = WebWidget::Create(delegate);
+  host->webwidget_->Resize(gfx::Size(content_rect.size.width,
+                                     content_rect.size.height));
+  return host;
+}
+
+/*static*/
+WebWidgetHost* WebWidgetHost::FromWindow(NSWindow* hwnd) {
+  return NULL;
+}
+
+/*static*/
+void WebWidgetHost::HandleEvent(NSWindow *window, NSEvent *event) {
+  WebWidgetHost* host = FromWindow(window);
+  if (host) {
+    switch ([event type]) {
+      case NSLeftMouseDown:
+      case NSLeftMouseUp:
+      case NSRightMouseDown:
+      case NSRightMouseUp:
+      case NSOtherMouseDown:
+      case NSOtherMouseUp:
+      case NSMouseEntered:
+      case NSMouseExited:
+        host->MouseEvent(event);
+        break;
+
+      case NSScrollWheel:
+        host->WheelEvent(event);
+        break;
+
+      case NSKeyDown:
+      case NSKeyUp:
+        host->KeyEvent(event);
+        break;
+
+      case NSAppKitDefined:
+        switch ([event subtype]) {
+          case NSApplicationActivatedEventType:
+            host->SetFocus(true);
+            break;
+          case NSApplicationDeactivatedEventType:
+            host->SetFocus(false);
+            break;
+        }
+        break;
+    }
+  }
+}
+
+void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) {
+  DLOG_IF(WARNING, painting_) << "unexpected invalidation while painting";
+
+  // If this invalidate overlaps with a pending scroll, then we have to
+  // downgrade to invalidating the scroll rect.
+  if (damaged_rect.Intersects(scroll_rect_)) {
+    paint_rect_ = paint_rect_.Union(scroll_rect_);
+    ResetScrollRect();
+  }
+  paint_rect_ = paint_rect_.Union(damaged_rect);
+
+  NSRect r = NSRectFromCGRect(damaged_rect.ToCGRect());
+  [view_ setNeedsDisplayInRect:r];
+}
+
+void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) {
+  DCHECK(dx || dy);
+
+  // If we already have a pending scroll operation or if this scroll operation
+  // intersects the existing paint region, then just failover to invalidating.
+  if (!scroll_rect_.IsEmpty() || paint_rect_.Intersects(clip_rect)) {
+    paint_rect_ = paint_rect_.Union(scroll_rect_);
+    ResetScrollRect();
+    paint_rect_ = paint_rect_.Union(clip_rect);
+  }
+
+  // We will perform scrolling lazily, when requested to actually paint.
+  scroll_rect_ = clip_rect;
+  scroll_dx_ = dx;
+  scroll_dy_ = dy;
+
+  NSRect r = NSRectFromCGRect(clip_rect.ToCGRect());
+  [view_ setNeedsDisplayInRect:r];
+}
+
+// void WebWidgetHost::SetCursor(HCURSOR cursor) {
+// }
+
+void WebWidgetHost::DiscardBackingStore() {
+  canvas_.reset();
+}
+
+WebWidgetHost::WebWidgetHost()
+    : view_(NULL),
+      webwidget_(NULL),
+      scroll_dx_(0),
+      scroll_dy_(0),
+      track_mouse_leave_(false)
+{
+  set_painting(false);
+}
+
+WebWidgetHost::~WebWidgetHost() {
+  // win_util::SetWindowUserData(hwnd_, 0);
+
+  TrackMouseLeave(false);
+
+  webwidget_->Close();
+  webwidget_->Release();
+}
+
+void WebWidgetHost::Paint() {
+  NSRect r = [view_ frame];
+  gfx::Rect client_rect(NSRectToCGRect(r));
+  NSGraphicsContext* view_context = [NSGraphicsContext currentContext];
+  CGContextRef context = static_cast<CGContextRef>([view_context graphicsPort]);
+
+  // Allocate a canvas if necessary
+  if (!canvas_.get()) {
+    ResetScrollRect();
+    paint_rect_ = client_rect;
+    canvas_.reset(new gfx::PlatformCanvas(
+        paint_rect_.width(), paint_rect_.height(), true));
+  }
+
+  // make sure webkit draws into our bitmap, not the window
+  CGContextRef bitmap_context =
+      canvas_->getTopPlatformDevice().GetBitmapContext();
+  [NSGraphicsContext setCurrentContext:
+      [NSGraphicsContext graphicsContextWithGraphicsPort:bitmap_context
+                                                 flipped:NO]];
+  
+  // This may result in more invalidation
+  webwidget_->Layout();
+  
+  // Scroll the canvas if necessary
+  scroll_rect_ = client_rect.Intersect(scroll_rect_);
+  if (!scroll_rect_.IsEmpty()) {
+    // add to invalidate rect, since there's no equivalent of ScrollDC.
+    paint_rect_.Union(scroll_rect_);
+  }
+  ResetScrollRect();
+
+  // Paint the canvas if necessary.  Allow painting to generate extra rects the
+  // first time we call it.  This is necessary because some WebCore rendering
+  // objects update their layout only when painted.
+  for (int i = 0; i < 2; ++i) {
+    paint_rect_ = client_rect;//.Intersect(paint_rect_);
+    if (!paint_rect_.IsEmpty()) {
+      gfx::Rect rect(paint_rect_);
+      paint_rect_ = gfx::Rect();
+
+//      DLOG_IF(WARNING, i == 1) << "painting caused additional invalidations";
+      PaintRect(rect);
+    }
+  }
+  DCHECK(paint_rect_.IsEmpty());
+  
+  // set the context back to our window
+  [NSGraphicsContext setCurrentContext: view_context];
+
+  // Paint to the screen
+  if ([view_ lockFocusIfCanDraw]) {
+    CGRect paint_rect = NSRectToCGRect(r);
+    int bitmap_height = CGBitmapContextGetHeight(bitmap_context);
+    int bitmap_width = CGBitmapContextGetWidth(bitmap_context);
+    CGRect bitmap_rect = { { 0, 0 },
+                           { bitmap_width, bitmap_height } };
+    canvas_->getTopPlatformDevice().DrawToContext(
+        context, 0, client_rect.height() - bitmap_height, &bitmap_rect);
+    
+    [view_ unlockFocus];
+  }
+
+  // Draw children
+  // UpdateWindow(hwnd_);
+}
+
+void WebWidgetHost::Resize(const gfx::Rect& rect) {
+  // Force an entire re-paint.  TODO(darin): Maybe reuse this memory buffer.
+  DiscardBackingStore();
+  webwidget_->Resize(gfx::Size(rect.width(), rect.height()));
+}
+
+void WebWidgetHost::MouseEvent(NSEvent *event) {
+  WebMouseEvent web_event(event);
+  switch (event.type) {
+    case WebInputEvent::MOUSE_MOVE:
+      TrackMouseLeave(true);
+      break;
+    case WebInputEvent::MOUSE_LEAVE:
+      TrackMouseLeave(false);
+      break;
+    case WebInputEvent::MOUSE_DOWN:
+      break;
+    case WebInputEvent::MOUSE_UP:
+      break;
+  }
+  webwidget_->HandleInputEvent(&web_event);
+}
+
+void WebWidgetHost::WheelEvent(NSEvent *event) {
+  WebMouseWheelEvent web_event(event);
+  webwidget_->HandleInputEvent(&web_event);
+}
+
+void WebWidgetHost::KeyEvent(NSEvent *event) {
+  WebKeyboardEvent web_event(event);
+  webwidget_->HandleInputEvent(&web_event);
+}
+
+void WebWidgetHost::SetFocus(bool enable) {
+  webwidget_->SetFocus(enable);
+}
+
+void WebWidgetHost::TrackMouseLeave(bool track) {
+}
+
+void WebWidgetHost::ResetScrollRect() {
+  scroll_rect_ = gfx::Rect();
+  scroll_dx_ = 0;
+  scroll_dy_ = 0;
+}
+
+void WebWidgetHost::PaintRect(const gfx::Rect& rect) {
+  DCHECK(!painting_);
+  DCHECK(canvas_.get());
+
+  set_painting(true);
+  webwidget_->Paint(canvas_.get(), rect);
+  set_painting(false);
+}
-- 
cgit v1.1