// Copyright (c) 2008-2009 The Chromium Embedded Framework Authors. // Portions copyright (c) 2006-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. // This file contains the implementation of BrowserWebViewDelegate, which serves // as the WebViewDelegate for the BrowserWebHost. The host is expected to // have initialized a MessageLoop before these methods are called. #include "precompiled_libcef.h" #include "config.h" #include "browser_webview_delegate.h" #include "browser_impl.h" #include "browser_navigation_controller.h" #include "context.h" #include "request_impl.h" #include "v8_impl.h" #include "base/file_util.h" #include "base/gfx/gdi_util.h" #include "base/gfx/point.h" #include "base/gfx/native_widget_types.h" #include "base/message_loop.h" #include "base/string_util.h" #include "base/trace_event.h" #include "net/base/net_errors.h" #include "webkit/api/public/WebDragData.h" #include "webkit/api/public/WebKit.h" #include "webkit/api/public/WebScreenInfo.h" #include "webkit/api/public/WebString.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 "browser_webkit_glue.h" #if defined(OS_WIN) // TODO(port): make these files work everywhere. #include "browser_drag_delegate.h" #include "browser_drop_delegate.h" #endif using WebKit::WebDragData; using WebKit::WebRect; using WebKit::WebScreenInfo; using WebKit::WebSize; using WebKit::WebString; using WebKit::WebWorker; using WebKit::WebWorkerClient; namespace { int next_page_id_ = 1; } // namespace // WebViewDelegate ----------------------------------------------------------- WebView* BrowserWebViewDelegate::CreateWebView(WebView* webview, bool user_gesture, const GURL& creator_url) { CefRefPtr browser = browser_->UIT_CreatePopupWindow(std::wstring()); return browser.get() ? browser->GetWebView() : NULL; } WebWidget* BrowserWebViewDelegate::CreatePopupWidget(WebView* webview, bool activatable) { return browser_->UIT_CreatePopupWidget(webview); } WebWorker* BrowserWebViewDelegate::CreateWebWorker(WebWorkerClient* client) { return NULL; } void BrowserWebViewDelegate::OpenURL(WebView* webview, const GURL& url, const GURL& referrer, WindowOpenDisposition disposition) { DCHECK_NE(disposition, CURRENT_TAB); // No code for this if (disposition == SUPPRESS_OPEN) return; CefRefPtr browser = browser_->UIT_CreatePopupWindow(UTF8ToWide(url.spec())); if(browser.get()) browser->UIT_Show(browser->GetWebView(), disposition); } void BrowserWebViewDelegate::DidStartLoading(WebView* webview) { // clear the title so we can tell if it wasn't provided by the page browser_->UIT_SetTitle(std::wstring()); CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { // Notify the handler that loading has started handler->HandleLoadStart(browser_, NULL); } } void BrowserWebViewDelegate::DidStopLoading(WebView* webview) { if(browser_->UIT_GetTitle().empty()) { // no title was provided by the page, so send a blank string to the client CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { // Notify the handler of a page title change handler->HandleTitleChange(browser_, browser_->UIT_GetTitle()); } } CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { // Notify the handler that loading has ended handler->HandleLoadEnd(browser_, NULL); } } void BrowserWebViewDelegate::WindowObjectCleared(WebFrame* webframe) { CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { v8::HandleScope handle_scope; v8::Handle context = webkit_glue::GetV8Context(webframe); if(context.IsEmpty()) return; v8::Context::Scope scope(context); CefRefPtr frame(browser_->GetCefFrame(webframe)); CefRefPtr object = new CefV8ValueImpl(context->Global()); handler->HandleJSBinding(browser_, frame, object); } } WindowOpenDisposition BrowserWebViewDelegate::DispositionForNavigationAction( WebView* webview, WebFrame* frame, const WebRequest* request, WebNavigationType type, WindowOpenDisposition disposition, bool is_redirect) { CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { // Gather browse request information CefRefPtr req(CefRequest::CreateRequest()); req->SetURL(UTF8ToWide(request->GetURL().spec())); req->SetMethod(UTF8ToWide(request->GetHttpMethod())); if(request->HasUploadData()) { scoped_refptr data(new net::UploadData()); request->GetUploadData(data.get()); CefRefPtr postdata(CefPostData::CreatePostData()); static_cast(postdata.get())->Set(*data.get()); req->SetPostData(postdata); } WebRequest::HeaderMap map; request->GetHttpHeaders(&map); if(map.size() > 0) static_cast(req.get())->SetHeaderMap(map); // Notify the handler of a browse request CefHandler::RetVal rv = handler->HandleBeforeBrowse(browser_, browser_->GetCefFrame(frame), req, (CefHandler::NavType)type, is_redirect); if(rv == RV_HANDLED) return IGNORE_ACTION; } WindowOpenDisposition result; if (policy_delegate_enabled_) { std::wstring frame_name = frame->GetName(); std::string url_description; if (request->GetURL().SchemeIs("file")) { url_description = request->GetURL().ExtractFileName(); } else { url_description = request->GetURL().spec(); } result = policy_delegate_is_permissive_ ? CURRENT_TAB : IGNORE_ACTION; } else { result = WebViewDelegate::DispositionForNavigationAction( webview, frame, request, type, disposition, is_redirect); } return result; } void BrowserWebViewDelegate::AssignIdentifierToRequest(WebView* webview, uint32 identifier, const WebRequest& request) { } void BrowserWebViewDelegate::WillSendRequest(WebView* webview, uint32 identifier, WebRequest* request) { // The requestor ID is used by the resource loader bridge to locate the // browser that originated the request. request->SetRequestorID(browser_->UIT_GetUniqueID()); } void BrowserWebViewDelegate::DidFinishLoading(WebView* webview, uint32 identifier) { } void BrowserWebViewDelegate::DidFailLoadingWithError(WebView* webview, uint32 identifier, const WebError& error) { } void BrowserWebViewDelegate::DidStartProvisionalLoadForFrame( WebView* webview, WebFrame* frame, NavigationGesture gesture) { if (!top_loading_frame_) { top_loading_frame_ = frame; } UpdateAddressBar(webview); } void BrowserWebViewDelegate::DidReceiveServerRedirectForProvisionalLoadForFrame( WebView* webview, WebFrame* frame) { UpdateAddressBar(webview); } void BrowserWebViewDelegate::DidFailProvisionalLoadWithError( WebView* webview, const WebError& error, WebFrame* frame) { LocationChangeDone(frame); // error codes are defined in net\base\net_error_list.h // 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(); BrowserExtraRequestData* extra_data = static_cast(failed_request.GetExtraData()); bool replace = extra_data && extra_data->pending_page_id != -1; scoped_ptr request(failed_request.Clone()); request->SetURL(GURL("cef-error:")); std::string error_text; CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { // give the handler an opportunity to generate a custom error message std::wstring error_str; CefHandler::RetVal rv = handler->HandleLoadError(browser_, browser_->GetCefFrame(frame), static_cast(error.GetErrorCode()), UTF8ToWide(error.GetFailedURL().spec()), error_str); if(rv == RV_HANDLED && !error_str.empty()) error_text = WideToUTF8(error_str); } else { error_text = StringPrintf("Error loading url: %d", error.GetErrorCode()); } frame->LoadAlternateHTMLString(request.get(), error_text, error.GetFailedURL(), replace); } void BrowserWebViewDelegate::DidCommitLoadForFrame(WebView* webview, WebFrame* frame, bool is_new_navigation) { UpdateForCommittedLoad(frame, is_new_navigation); CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { // Notify the handler that loading has started handler->HandleLoadStart(browser_, browser_->GetCefFrame(frame)); } } void BrowserWebViewDelegate::DidReceiveTitle(WebView* webview, const std::wstring& title, WebFrame* frame) { browser_->UIT_SetTitle(title); CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { // Notify the handler of a page title change handler->HandleTitleChange(browser_, title); } } void BrowserWebViewDelegate::DidFinishLoadForFrame(WebView* webview, WebFrame* frame) { UpdateAddressBar(webview); LocationChangeDone(frame); CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { // Notify the handler that loading has ended handler->HandleLoadEnd(browser_, browser_->GetCefFrame(frame)); } } void BrowserWebViewDelegate::DidFailLoadWithError(WebView* webview, const WebError& error, WebFrame* frame) { LocationChangeDone(frame); } void BrowserWebViewDelegate::DidFinishDocumentLoadForFrame(WebView* webview, WebFrame* frame) { } void BrowserWebViewDelegate::DidHandleOnloadEventsForFrame(WebView* webview, WebFrame* frame) { } void BrowserWebViewDelegate::DidChangeLocationWithinPageForFrame( WebView* webview, WebFrame* frame, bool is_new_navigation) { UpdateForCommittedLoad(frame, is_new_navigation); } void BrowserWebViewDelegate::DidReceiveIconForFrame(WebView* webview, WebFrame* frame) { } void BrowserWebViewDelegate::WillPerformClientRedirect(WebView* webview, WebFrame* frame, const std::wstring& dest_url, unsigned int delay_seconds, unsigned int fire_date) { } void BrowserWebViewDelegate::DidCancelClientRedirect(WebView* webview, WebFrame* frame) { } void BrowserWebViewDelegate::AddMessageToConsole(WebView* webview, const std::wstring& message, unsigned int line_no, const std::wstring& source_id) { logging::LogMessage("CONSOLE", 0).stream() << "\"" << message.c_str() << ",\" source: " << source_id.c_str() << "(" << line_no << ")"; } void BrowserWebViewDelegate::RunJavaScriptAlert(WebFrame* webframe, const std::wstring& message) { CefHandler::RetVal rv = RV_CONTINUE; CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { rv = handler->HandleJSAlert(browser_, browser_->GetCefFrame(webframe), message); } if(rv != RV_HANDLED) ShowJavaScriptAlert(webframe, message); } bool BrowserWebViewDelegate::RunJavaScriptConfirm(WebFrame* webframe, const std::wstring& message) { CefHandler::RetVal rv = RV_CONTINUE; bool retval = false; CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { rv = handler->HandleJSConfirm(browser_, browser_->GetCefFrame(webframe), message, retval); } if(rv != RV_HANDLED) retval = ShowJavaScriptConfirm(webframe, message); return retval; } bool BrowserWebViewDelegate::RunJavaScriptPrompt(WebFrame* webframe, const std::wstring& message, const std::wstring& default_value, std::wstring* result) { CefHandler::RetVal rv = RV_CONTINUE; bool retval = false; CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { rv = handler->HandleJSPrompt(browser_, browser_->GetCefFrame(webframe), message, default_value, retval, *result); } if(rv != RV_HANDLED) retval = ShowJavaScriptPrompt(webframe, message, default_value, result); return retval; } void BrowserWebViewDelegate::SetStatusbarText(WebView* webview, const std::wstring& message) { } void BrowserWebViewDelegate::StartDragging(WebView* webview, const WebDragData& drag_data) { #if defined(OS_WIN) // TODO(port): make this work on all platforms. if (!drag_delegate_) { drag_delegate_ = new BrowserDragDelegate( browser_->GetWebViewWndHandle(), browser_->GetWebView()); } // TODO(tc): Drag and drop is disabled in the test shell because we need // to be able to convert from WebDragData to an IDataObject. //const DWORD ok_effect = DROPEFFECT_COPY | DROPEFFECT_LINK | DROPEFFECT_MOVE; //DWORD effect; //HRESULT res = DoDragDrop(drag_data.data_object, drag_delegate_.get(), // ok_effect, &effect); //DCHECK(DRAGDROP_S_DROP == res || DRAGDROP_S_CANCEL == res); webview->DragSourceSystemDragEnded(); #endif } // The output from these methods in non-interactive mode should match that // expected by the layout tests. See EditingDelegate.m in DumpRenderTree. bool BrowserWebViewDelegate::ShouldBeginEditing(WebView* webview, std::wstring range) { return browser_->UIT_AllowEditing(); } bool BrowserWebViewDelegate::ShouldEndEditing(WebView* webview, std::wstring range) { return browser_->UIT_AllowEditing(); } bool BrowserWebViewDelegate::ShouldInsertNode(WebView* webview, std::wstring node, std::wstring range, std::wstring action) { return browser_->UIT_AllowEditing(); } bool BrowserWebViewDelegate::ShouldInsertText(WebView* webview, std::wstring text, std::wstring range, std::wstring action) { return browser_->UIT_AllowEditing(); } bool BrowserWebViewDelegate::ShouldChangeSelectedRange(WebView* webview, std::wstring fromRange, std::wstring toRange, std::wstring affinity, bool stillSelecting) { return browser_->UIT_AllowEditing(); } bool BrowserWebViewDelegate::ShouldDeleteRange(WebView* webview, std::wstring range) { return browser_->UIT_AllowEditing(); } bool BrowserWebViewDelegate::ShouldApplyStyle(WebView* webview, std::wstring style, std::wstring range) { return browser_->UIT_AllowEditing(); } bool BrowserWebViewDelegate::SmartInsertDeleteEnabled() { return true; } void BrowserWebViewDelegate::DidBeginEditing() { } void BrowserWebViewDelegate::DidChangeSelection() { } void BrowserWebViewDelegate::DidChangeContents() { } void BrowserWebViewDelegate::DidEndEditing() { } void BrowserWebViewDelegate::NavigateBackForwardSoon(int offset) { browser_->UIT_GetNavigationController()->GoToOffset(offset); } int BrowserWebViewDelegate::GetHistoryBackListCount() { int current_index = browser_->UIT_GetNavigationController()->GetLastCommittedEntryIndex(); return current_index; } int BrowserWebViewDelegate::GetHistoryForwardListCount() { int current_index = browser_->UIT_GetNavigationController()->GetLastCommittedEntryIndex(); return browser_->UIT_GetNavigationController()->GetEntryCount() - current_index - 1; } void BrowserWebViewDelegate::SetUserStyleSheetEnabled(bool is_enabled) { WebPreferences* prefs = _Context->GetWebPreferences(); prefs->user_style_sheet_enabled = is_enabled; browser_->GetWebView()->SetPreferences(*prefs); } void BrowserWebViewDelegate::SetUserStyleSheetLocation(const GURL& location) { WebPreferences* prefs = _Context->GetWebPreferences(); prefs->user_style_sheet_enabled = true; prefs->user_style_sheet_location = location; browser_->GetWebView()->SetPreferences(*prefs); } // WebWidgetDelegate --------------------------------------------------------- gfx::NativeViewId BrowserWebViewDelegate::GetContainingView(WebWidget* webwidget) { if (WebWidgetHost* host = GetHostForWidget(webwidget)) return gfx::IdFromNativeView(host->window_handle()); return NULL; } void BrowserWebViewDelegate::DidInvalidateRect(WebWidget* webwidget, const WebRect& rect) { if (WebWidgetHost* host = GetHostForWidget(webwidget)) host->DidInvalidateRect(rect); } void BrowserWebViewDelegate::DidScrollRect(WebWidget* webwidget, int dx, int dy, const WebRect& clip_rect) { if (WebWidgetHost* host = GetHostForWidget(webwidget)) host->DidScrollRect(dx, dy, clip_rect); } void BrowserWebViewDelegate::Focus(WebWidget* webwidget) { if (WebWidgetHost* host = GetHostForWidget(webwidget)) browser_->UIT_SetFocus(host, true); } void BrowserWebViewDelegate::Blur(WebWidget* webwidget) { if (WebWidgetHost* host = GetHostForWidget(webwidget)) browser_->UIT_SetFocus(host, false); } bool BrowserWebViewDelegate::IsHidden(WebWidget* webwidget) { return false; } WebScreenInfo BrowserWebViewDelegate::GetScreenInfo(WebWidget* webwidget) { if (WebWidgetHost* host = GetHostForWidget(webwidget)) return host->GetScreenInfo(); return WebScreenInfo(); } void BrowserWebViewDelegate::SetSmartInsertDeleteEnabled(bool enabled) { smart_insert_delete_enabled_ = enabled; // In upstream WebKit, smart insert/delete is mutually exclusive with select // trailing whitespace, however, we allow both because Chromium on Windows // allows both. } void BrowserWebViewDelegate::SetSelectTrailingWhitespaceEnabled(bool enabled) { select_trailing_whitespace_enabled_ = enabled; // In upstream WebKit, smart insert/delete is mutually exclusive with select // trailing whitespace, however, we allow both because Chromium on Windows // allows both. } void BrowserWebViewDelegate::RegisterDragDrop() { #if defined(OS_WIN) // TODO(port): add me once drag and drop works. DCHECK(!drop_delegate_); drop_delegate_ = new BrowserDropDelegate(browser_->GetWebViewWndHandle(), browser_->GetWebView()); #endif } void BrowserWebViewDelegate::SetCustomPolicyDelegate(bool is_custom, bool is_permissive) { policy_delegate_enabled_ = is_custom; policy_delegate_is_permissive_ = is_permissive; } void BrowserWebViewDelegate::WaitForPolicyDelegate() { policy_delegate_enabled_ = true; } // Private methods ----------------------------------------------------------- void BrowserWebViewDelegate::UpdateAddressBar(WebView* webView) { /* WebFrame* mainFrame = webView->UIT_GetMainFrame(); WebDataSource* dataSource = mainFrame->GetDataSource(); if (!dataSource) dataSource = mainFrame->GetProvisionalDataSource(); if (!dataSource) return; GURL gUrl = dataSource->GetRequest().GetMainDocumentURL(); */ } void BrowserWebViewDelegate::LocationChangeDone(WebFrame* frame) { if (frame == top_loading_frame_) top_loading_frame_ = NULL; } WebWidgetHost* BrowserWebViewDelegate::GetHostForWidget(WebWidget* webwidget) { if (webwidget == browser_->GetWebView()) return browser_->GetWebViewHost(); if (webwidget == browser_->GetPopup()) return browser_->GetPopupHost(); return NULL; } void BrowserWebViewDelegate::UpdateForCommittedLoad(WebFrame* frame, bool is_new_navigation) { WebView* webview = browser_->GetWebView(); // Code duplicated from RenderView::DidCommitLoadForFrame. const WebRequest& request = webview->GetMainFrame()->GetDataSource()->GetRequest(); BrowserExtraRequestData* extra_data = static_cast(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 BrowserWebViewDelegate::UpdateURL(WebFrame* frame) { WebDataSource* ds = frame->GetDataSource(); DCHECK(ds); const WebRequest& request = ds->GetRequest(); // Type is unused. scoped_ptr entry(new BrowserNavigationEntry); // 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())); } std::wstring url = UTF8ToWide(entry->GetURL().spec().c_str()); CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { // Notify the handler of an address change handler->HandleAddressChange(browser_, browser_->GetCefFrame(frame), url); } browser_->UIT_GetNavigationController()->DidNavigateToEntry(entry.release()); last_page_id_updated_ = std::max(last_page_id_updated_, page_id_); } void BrowserWebViewDelegate::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; BrowserNavigationEntry* entry = static_cast( browser_->UIT_GetNavigationController()->GetEntryWithPageID(page_id_)); if (!entry) return; std::string state; if (!browser_->GetWebView()->GetMainFrame()-> GetPreviousHistoryState(&state)) return; entry->SetContentState(state); } std::wstring BrowserWebViewDelegate::GetFrameDescription(WebFrame* webframe) { std::wstring name = webframe->GetName(); if (webframe == browser_->GetWebView()->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)"; } } void BrowserWebViewDelegate::TakeFocus(WebView* webview, bool reverse) { CefRefPtr handler = browser_->GetHandler(); if(handler.get()) { // Notify the handler that it should take a focus handler->HandleTakeFocus(browser_, reverse); } }