///////////////////////////////////////////////////////////////////////////// // Author: Steven Lamerton // Copyright: (c) 2013 Steven Lamerton // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// #include "wx/wxprec.h" #include #include #include #include #include #ifdef __VISUALC__ #pragma warning(push) #pragma warning(disable:4100) #endif #include #include #include #include #ifdef __VISUALC__ #pragma warning(pop) #endif extern WXDLLIMPEXP_DATA_WEBVIEW(const char) wxWebViewBackendChromium[] = "wxWebViewChromium"; wxIMPLEMENT_DYNAMIC_CLASS(wxWebViewChromium, wxWebView); class wxStringVisitor : public CefStringVisitor { public: enum StringType { PAGE_SOURCE, PAGE_TEXT, }; wxStringVisitor(wxWebViewChromium* webview, StringType type) : m_webview(webview), m_type(type) {} void Visit(const CefString& string) { switch(m_type) { case PAGE_SOURCE: m_webview->SetPageSource(string.ToWString()); break; case PAGE_TEXT: m_webview->SetPageText(string.ToWString()); break; } } private: StringType m_type; wxWebViewChromium *m_webview; IMPLEMENT_REFCOUNTING(wxStringVisitor); }; bool wxWebViewChromium::Create(wxWindow* parent, wxWindowID id, const wxString& url, const wxPoint& pos, const wxSize& size, long style, const wxString& name) { if (!wxControl::Create(parent, id, pos, size, style, wxDefaultValidator, name)) { return false; } m_historyLoadingFromList = false; m_historyEnabled = true; m_historyPosition = -1; m_zoomLevel = wxWEBVIEW_ZOOM_MEDIUM; CefBrowserSettings browsersettings; CefWindowInfo info; m_clientHandler = new ClientHandler(); m_clientHandler->SetWebView(this); #ifdef __WXMSW__ // Initialize window info to the defaults for a child window info.SetAsChild(GetHWND(), wxGetClientRect(this->GetHWND())); #endif // Creat the new child browser window, we do this async as we use a multi // threaded message loop #if CHROME_VERSION_BUILD >= 1650 CefBrowserHost::CreateBrowser(info, static_cast>(m_clientHandler), url.ToStdString(), browsersettings, NULL); #else CefBrowserHost::CreateBrowser(info, static_cast>(m_clientHandler), url.ToStdString(), browsersettings); #endif this->Bind(wxEVT_SIZE, &wxWebViewChromium::OnSize, this); return true; } wxWebViewChromium::~wxWebViewChromium() { CefRefPtr browser = m_clientHandler->GetBrowser(); if ( browser.get() ) { // Let the browser window know we are about to destroy it. browser->GetHost()->ParentWindowWillClose(); } } void wxWebViewChromium::OnSize(wxSizeEvent& event) { wxSize size = GetClientSize(); #ifdef __WXMSW__ if ( m_clientHandler && m_clientHandler->GetBrowser() && m_clientHandler->GetBrowser()->GetHost() ) { HWND handle = m_clientHandler->GetBrowser()->GetHost()->GetWindowHandle(); if ( handle ) { HDWP hdwp = BeginDeferWindowPos(1); hdwp = DeferWindowPos(hdwp, handle, NULL, 0, 0, size.GetWidth(), size.GetHeight(), SWP_NOZORDER); EndDeferWindowPos(hdwp); } } #endif event.Skip(); } void wxWebViewChromium::SetPageSource(const wxString& pageSource) { m_pageSource = pageSource; } void wxWebViewChromium::SetPageText(const wxString& pageText) { m_pageText = pageText; } void* wxWebViewChromium::GetNativeBackend() const { return m_clientHandler->GetBrowser(); } bool wxWebViewChromium::CanGoForward() const { if ( m_historyEnabled ) return m_historyPosition != static_cast(m_historyList.size()) - 1; else return false; } bool wxWebViewChromium::CanGoBack() const { if ( m_historyEnabled ) return m_historyPosition > 0; else return false; } void wxWebViewChromium::LoadHistoryItem(wxSharedPtr item) { int pos = -1; for ( unsigned int i = 0; i < m_historyList.size(); i++ ) { //We compare the actual pointers to find the correct item if(m_historyList[i].get() == item.get()) pos = i; } wxASSERT_MSG(pos != static_cast(m_historyList.size()), "invalid history item"); m_historyLoadingFromList = true; LoadURL(item->GetUrl()); m_historyPosition = pos; } wxVector > wxWebViewChromium::GetBackwardHistory() { wxVector > backhist; //As we don't have std::copy or an iterator constructor in the wxwidgets //native vector we construct it by hand for ( int i = 0; i < m_historyPosition; i++ ) { backhist.push_back(m_historyList[i]); } return backhist; } wxVector > wxWebViewChromium::GetForwardHistory() { wxVector > forwardhist; //As we don't have std::copy or an iterator constructor in the wxwidgets //native vector we construct it by hand for ( int i = m_historyPosition + 1; i < static_cast(m_historyList.size()); i++ ) { forwardhist.push_back(m_historyList[i]); } return forwardhist; } void wxWebViewChromium::GoBack() { LoadHistoryItem(m_historyList[m_historyPosition - 1]); } void wxWebViewChromium::GoForward() { LoadHistoryItem(m_historyList[m_historyPosition + 1]); } void wxWebViewChromium::LoadURL(const wxString& url) { m_clientHandler->GetBrowser()->GetMainFrame()->LoadURL(url.ToStdString()); } void wxWebViewChromium::ClearHistory() { m_historyList.clear(); m_historyPosition = -1; } void wxWebViewChromium::EnableHistory(bool enable) { m_historyEnabled = enable; } void wxWebViewChromium::Stop() { m_clientHandler->GetBrowser()->StopLoad(); } void wxWebViewChromium::Reload(wxWebViewReloadFlags flags) { if ( flags == wxWEBVIEW_RELOAD_NO_CACHE ) { m_clientHandler->GetBrowser()->ReloadIgnoreCache(); } else { m_clientHandler->GetBrowser()->Reload(); } } wxString wxWebViewChromium::GetPageSource() const { return m_pageSource; } wxString wxWebViewChromium::GetPageText() const { return m_pageText; } wxString wxWebViewChromium::GetCurrentURL() const { return m_clientHandler->GetBrowser()->GetMainFrame()->GetURL().ToString(); } wxString wxWebViewChromium::GetCurrentTitle() const { return m_title; } void wxWebViewChromium::Print() { #if CHROME_VERSION_BUILD >= 1650 m_clientHandler->GetBrowser()->GetHost()->Print(); #endif } void wxWebViewChromium::Cut() { m_clientHandler->GetBrowser()->GetMainFrame()->Cut(); } void wxWebViewChromium::Copy() { m_clientHandler->GetBrowser()->GetMainFrame()->Copy(); } void wxWebViewChromium::Paste() { m_clientHandler->GetBrowser()->GetMainFrame()->Paste(); } void wxWebViewChromium::Undo() { m_clientHandler->GetBrowser()->GetMainFrame()->Undo(); } void wxWebViewChromium::Redo() { m_clientHandler->GetBrowser()->GetMainFrame()->Redo(); } void wxWebViewChromium::SelectAll() { m_clientHandler->GetBrowser()->GetMainFrame()->SelectAll(); } void wxWebViewChromium::DeleteSelection() { wxString jsdelete = "if (window.getSelection) { if (window.getSelection().deleteFromDocument) { window.getSelection().deleteFromDocument(); } }"; RunScript(jsdelete); } void wxWebViewChromium::ClearSelection() { wxString jsclear = "if (window.getSelection) { if (window.getSelection().empty) { window.getSelection().empty(); } }"; RunScript(jsclear); } void wxWebViewChromium::RunScript(const wxString& javascript) { m_clientHandler->GetBrowser()->GetMainFrame()->ExecuteJavaScript(javascript.ToStdString(), "", 0); } bool wxWebViewChromium::IsBusy() const { if(m_clientHandler->GetBrowser()) return m_clientHandler->GetBrowser()->IsLoading(); else return false; } void wxWebViewChromium::SetEditable(bool enable) { wxString mode = enable ? "\"on\"" : "\"off\""; RunScript("document.designMode = " + mode); } void wxWebViewChromium::DoSetPage(const wxString& html, const wxString& baseUrl) { m_clientHandler->GetBrowser()->GetMainFrame()->LoadString(html.ToStdString(), baseUrl.ToStdString()); } wxWebViewZoom wxWebViewChromium::GetZoom() const { return m_zoomLevel; } void wxWebViewChromium::SetZoom(wxWebViewZoom zoom) { m_zoomLevel = zoom; double mapzoom; // arbitrary way to map our common zoom enum to float zoom switch ( zoom ) { case wxWEBVIEW_ZOOM_TINY: mapzoom = -1.0; break; case wxWEBVIEW_ZOOM_SMALL: mapzoom = -0.5; break; case wxWEBVIEW_ZOOM_MEDIUM: mapzoom = 0.0; break; case wxWEBVIEW_ZOOM_LARGE: mapzoom = 0.5; break; case wxWEBVIEW_ZOOM_LARGEST: mapzoom = 1.0; break; default: wxASSERT(false); } m_clientHandler->GetBrowser()->GetHost()->SetZoomLevel(mapzoom); } void wxWebViewChromium::SetZoomType(wxWebViewZoomType type) { // there is only one supported zoom type at the moment so this setter // does nothing beyond checking sanity wxASSERT(type == wxWEBVIEW_ZOOM_TYPE_LAYOUT); } wxWebViewZoomType wxWebViewChromium::GetZoomType() const { return wxWEBVIEW_ZOOM_TYPE_LAYOUT; } bool wxWebViewChromium::CanSetZoomType(wxWebViewZoomType type) const { return type == wxWEBVIEW_ZOOM_TYPE_LAYOUT; } void wxWebViewChromium::RegisterHandler(wxSharedPtr handler) { // We currently don't support custom scheme handlers } bool wxWebViewChromium::StartUp(int &code, const wxString &path) { CefMainArgs args(wxGetInstance()); // If there is no subprocess then we need to execute on this process if ( path == "" ) { code = CefExecuteProcess(args, NULL); if ( code >= 0 ) return false; } CefSettings settings; // We use a multithreaded message loop so we don't have to integrate // with the wx message loop settings.multi_threaded_message_loop = true; CefString(&settings.browser_subprocess_path) = path.ToStdString(); return CefInitialize(args, settings, NULL); } int wxWebViewChromium::StartUpSubprocess() { CefMainArgs args(wxGetInstance()); return CefExecuteProcess(args, NULL); } void wxWebViewChromium::Shutdown() { CefShutdown(); } // CefDisplayHandler methods void ClientHandler::OnLoadingStateChange(CefRefPtr browser, bool isLoading, bool canGoBack, bool canGoForward) {} void ClientHandler::OnAddressChange(CefRefPtr browser, CefRefPtr frame, const CefString& url) {} void ClientHandler::OnTitleChange(CefRefPtr browser, const CefString& title) { m_webview->m_title = title.ToWString(); wxString target = browser->GetMainFrame()->GetName().ToString(); wxWebViewEvent event(wxEVT_COMMAND_WEBVIEW_TITLE_CHANGED, m_webview->GetId(), "", target); event.SetString(title.ToWString()); event.SetEventObject(m_webview); m_webview->HandleWindowEvent(event); } bool ClientHandler::OnConsoleMessage(CefRefPtr browser, const CefString& message, const CefString& source, int line) { return false; } // CefContextMenuHandler methods void ClientHandler::OnBeforeContextMenu(CefRefPtr browser, CefRefPtr frame, CefRefPtr params, CefRefPtr model) { if(!m_webview->IsContextMenuEnabled()) model->Clear(); } bool ClientHandler::OnContextMenuCommand(CefRefPtr browser, CefRefPtr frame, CefRefPtr params, int command_id, CefContextMenuHandler::EventFlags event_flags) { return false; } void ClientHandler::OnContextMenuDismissed(CefRefPtr browser, CefRefPtr frame) {} // CefLifeSpanHandler methods bool ClientHandler::OnBeforePopup(CefRefPtr browser, CefRefPtr frame, const CefString& target_url, const CefString& target_frame_name, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, CefRefPtr& client, CefBrowserSettings& settings, bool* no_javascript_access) { wxWebViewEvent *event = new wxWebViewEvent(wxEVT_WEBVIEW_NEWWINDOW, m_webview->GetId(), target_url.ToString(), target_frame_name.ToString()); event->SetEventObject(m_webview); // We use queue event as this function is called on the render thread m_webview->GetEventHandler()->QueueEvent(event); return true; } void ClientHandler::OnAfterCreated(CefRefPtr browser) { if ( !m_browser.get() ) { m_browser = browser; m_browserId = browser->GetIdentifier(); } } bool ClientHandler::DoClose(CefRefPtr browser) { return false; } void ClientHandler::OnBeforeClose(CefRefPtr browser) { if ( browser->GetIdentifier() == m_browserId ) { m_browser = NULL; } } // CefLoadHandler methods void ClientHandler::OnLoadStart(CefRefPtr browser, CefRefPtr frame) { wxString url = frame->GetURL().ToString(); wxString target = frame->GetName().ToString(); wxWebViewEvent event(wxEVT_COMMAND_WEBVIEW_NAVIGATING, m_webview->GetId(), url, target); event.SetEventObject(m_webview); m_webview->HandleWindowEvent(event); if ( !event.IsAllowed() ) { // We do not yet have support for vetoing pages } } void ClientHandler::OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) { wxString url = frame->GetURL().ToString(); wxString target = frame->GetName().ToString(); // Send webview_error event in case of loading error. if ( m_loadErrorCode != -1 ) { m_loadErrorCode = -1; wxWebViewEvent event(wxEVT_COMMAND_WEBVIEW_ERROR, m_webview->GetId(), url, target); event.SetEventObject(m_webview); m_webview->HandleWindowEvent(event); } wxWebViewEvent event(wxEVT_COMMAND_WEBVIEW_NAVIGATED, m_webview->GetId(), url, target); event.SetEventObject(m_webview); m_webview->HandleWindowEvent(event); if ( frame->IsMain() ) { //Get source code when main frame loads ended. CefRefPtr source_visitor = new wxStringVisitor( m_webview, wxStringVisitor::PAGE_SOURCE); frame->GetSource(source_visitor); //Get page text when main frame loads ended. CefRefPtr text_visitor = new wxStringVisitor( m_webview, wxStringVisitor::PAGE_TEXT); frame->GetText(text_visitor); //As we are complete we also add to the history list, but not if the //page is not the main page, ie it is a subframe if ( m_webview->m_historyEnabled && !m_webview->m_historyLoadingFromList ) { //If we are not at the end of the list, then erase everything //between us and the end before adding the new page if ( m_webview->m_historyPosition != static_cast(m_webview->m_historyList.size()) - 1 ) { m_webview->m_historyList.erase(m_webview->m_historyList.begin() + m_webview->m_historyPosition + 1, m_webview->m_historyList.end()); } wxSharedPtr item(new wxWebViewHistoryItem(url, m_webview->GetCurrentTitle())); m_webview->m_historyList.push_back(item); m_webview->m_historyPosition++; } //Reset as we are done now m_webview->m_historyLoadingFromList = false; wxWebViewEvent levent(wxEVT_COMMAND_WEBVIEW_LOADED, m_webview->GetId(), url, target); levent.SetEventObject(m_webview); m_webview->HandleWindowEvent(levent); } } void ClientHandler::OnLoadError(CefRefPtr browser, CefRefPtr frame, ErrorCode errorCode, const CefString& errorText, const CefString& failedUrl) { //We define a macro for convenience #define ERROR_TYPE_CASE(error, wxtype) case(error): \ type = wxtype;\ break wxWebViewNavigationError type = wxWEBVIEW_NAV_ERR_OTHER; switch ( errorCode ) { ERROR_TYPE_CASE(ERR_FAILED, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_ABORTED, wxWEBVIEW_NAV_ERR_USER_CANCELLED); ERROR_TYPE_CASE(ERR_INVALID_ARGUMENT, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_INVALID_HANDLE, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_FILE_NOT_FOUND, wxWEBVIEW_NAV_ERR_NOT_FOUND); ERROR_TYPE_CASE(ERR_TIMED_OUT, wxWEBVIEW_NAV_ERR_CONNECTION); ERROR_TYPE_CASE(ERR_FILE_TOO_BIG, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_UNEXPECTED, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_ACCESS_DENIED, wxWEBVIEW_NAV_ERR_AUTH); ERROR_TYPE_CASE(ERR_NOT_IMPLEMENTED, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_CONNECTION_CLOSED, wxWEBVIEW_NAV_ERR_CONNECTION); ERROR_TYPE_CASE(ERR_CONNECTION_RESET, wxWEBVIEW_NAV_ERR_CONNECTION); ERROR_TYPE_CASE(ERR_CONNECTION_REFUSED, wxWEBVIEW_NAV_ERR_CONNECTION); ERROR_TYPE_CASE(ERR_CONNECTION_ABORTED, wxWEBVIEW_NAV_ERR_CONNECTION); ERROR_TYPE_CASE(ERR_CONNECTION_FAILED, wxWEBVIEW_NAV_ERR_CONNECTION); ERROR_TYPE_CASE(ERR_NAME_NOT_RESOLVED, wxWEBVIEW_NAV_ERR_CONNECTION); ERROR_TYPE_CASE(ERR_INTERNET_DISCONNECTED, wxWEBVIEW_NAV_ERR_CONNECTION); ERROR_TYPE_CASE(ERR_SSL_PROTOCOL_ERROR, wxWEBVIEW_NAV_ERR_SECURITY); ERROR_TYPE_CASE(ERR_ADDRESS_INVALID, wxWEBVIEW_NAV_ERR_REQUEST); ERROR_TYPE_CASE(ERR_ADDRESS_UNREACHABLE, wxWEBVIEW_NAV_ERR_CONNECTION); ERROR_TYPE_CASE(ERR_SSL_CLIENT_AUTH_CERT_NEEDED, wxWEBVIEW_NAV_ERR_AUTH); ERROR_TYPE_CASE(ERR_TUNNEL_CONNECTION_FAILED, wxWEBVIEW_NAV_ERR_CONNECTION); ERROR_TYPE_CASE(ERR_NO_SSL_VERSIONS_ENABLED, wxWEBVIEW_NAV_ERR_SECURITY); ERROR_TYPE_CASE(ERR_SSL_VERSION_OR_CIPHER_MISMATCH, wxWEBVIEW_NAV_ERR_SECURITY); ERROR_TYPE_CASE(ERR_SSL_RENEGOTIATION_REQUESTED, wxWEBVIEW_NAV_ERR_REQUEST); ERROR_TYPE_CASE(ERR_CERT_COMMON_NAME_INVALID, wxWEBVIEW_NAV_ERR_CERTIFICATE); ERROR_TYPE_CASE(ERR_CERT_DATE_INVALID, wxWEBVIEW_NAV_ERR_CERTIFICATE); ERROR_TYPE_CASE(ERR_CERT_AUTHORITY_INVALID, wxWEBVIEW_NAV_ERR_CERTIFICATE); ERROR_TYPE_CASE(ERR_CERT_CONTAINS_ERRORS, wxWEBVIEW_NAV_ERR_CERTIFICATE); ERROR_TYPE_CASE(ERR_CERT_NO_REVOCATION_MECHANISM, wxWEBVIEW_NAV_ERR_CERTIFICATE); ERROR_TYPE_CASE(ERR_CERT_UNABLE_TO_CHECK_REVOCATION, wxWEBVIEW_NAV_ERR_CERTIFICATE); ERROR_TYPE_CASE(ERR_CERT_REVOKED, wxWEBVIEW_NAV_ERR_CERTIFICATE); ERROR_TYPE_CASE(ERR_CERT_INVALID, wxWEBVIEW_NAV_ERR_CERTIFICATE); ERROR_TYPE_CASE(ERR_CERT_END, wxWEBVIEW_NAV_ERR_CERTIFICATE); ERROR_TYPE_CASE(ERR_INVALID_URL, wxWEBVIEW_NAV_ERR_REQUEST); ERROR_TYPE_CASE(ERR_DISALLOWED_URL_SCHEME, wxWEBVIEW_NAV_ERR_REQUEST); ERROR_TYPE_CASE(ERR_UNKNOWN_URL_SCHEME, wxWEBVIEW_NAV_ERR_REQUEST); ERROR_TYPE_CASE(ERR_TOO_MANY_REDIRECTS, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_UNSAFE_REDIRECT, wxWEBVIEW_NAV_ERR_SECURITY); ERROR_TYPE_CASE(ERR_UNSAFE_PORT, wxWEBVIEW_NAV_ERR_SECURITY); ERROR_TYPE_CASE(ERR_INVALID_RESPONSE, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_INVALID_CHUNKED_ENCODING, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_METHOD_NOT_SUPPORTED, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_UNEXPECTED_PROXY_AUTH, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_EMPTY_RESPONSE, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_RESPONSE_HEADERS_TOO_BIG, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_CACHE_MISS, wxWEBVIEW_NAV_ERR_OTHER); ERROR_TYPE_CASE(ERR_INSECURE_RESPONSE, wxWEBVIEW_NAV_ERR_SECURITY); } m_loadErrorCode = type; }