// Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. #include "cefclient/client_handler.h" #include #include #include #include "include/cef_browser.h" #include "include/cef_frame.h" #include "include/wrapper/cef_stream_resource_handler.h" #include "cefclient/binding_test.h" #include "cefclient/cefclient.h" #include "cefclient/dom_test.h" #include "cefclient/resource_util.h" #include "cefclient/string_util.h" // Custom menu command Ids. enum client_menu_ids { CLIENT_ID_SHOW_DEVTOOLS = MENU_ID_USER_FIRST, CLIENT_ID_TESTMENU_SUBMENU, CLIENT_ID_TESTMENU_CHECKITEM, CLIENT_ID_TESTMENU_RADIOITEM1, CLIENT_ID_TESTMENU_RADIOITEM2, CLIENT_ID_TESTMENU_RADIOITEM3, }; ClientHandler::ClientHandler() : m_MainHwnd(NULL), m_BrowserId(0), m_EditHwnd(NULL), m_BackHwnd(NULL), m_ForwardHwnd(NULL), m_StopHwnd(NULL), m_ReloadHwnd(NULL), m_bFormElementHasFocus(false) { CreateProcessMessageDelegates(process_message_delegates_); CreateRequestDelegates(request_delegates_); } ClientHandler::~ClientHandler() { } bool ClientHandler::OnProcessMessageRecieved( CefRefPtr browser, CefProcessId source_process, CefRefPtr message) { bool handled = false; // Execute delegate callbacks. ProcessMessageDelegateSet::iterator it = process_message_delegates_.begin(); for (; it != process_message_delegates_.end() && !handled; ++it) { handled = (*it)->OnProcessMessageRecieved(this, browser, source_process, message); } return handled; } void ClientHandler::OnAfterCreated(CefRefPtr browser) { REQUIRE_UI_THREAD(); AutoLock lock_scope(this); if (!m_Browser.get()) { // We need to keep the main child window, but not popup windows m_Browser = browser; m_BrowserId = browser->GetIdentifier(); } } bool ClientHandler::DoClose(CefRefPtr browser) { REQUIRE_UI_THREAD(); if (m_BrowserId == browser->GetIdentifier()) { // Since the main window contains the browser window, we need to close // the parent window instead of the browser window. CloseMainWindow(); // Return true here so that we can skip closing the browser window // in this pass. (It will be destroyed due to the call to close // the parent above.) return true; } // A popup browser window is not contained in another window, so we can let // these windows close by themselves. return false; } void ClientHandler::OnBeforeClose(CefRefPtr browser) { REQUIRE_UI_THREAD(); if (m_BrowserId == browser->GetIdentifier()) { // Free the browser pointer so that the browser can be destroyed m_Browser = NULL; } else if (browser->IsPopup()) { // Remove the record for DevTools popup windows. std::set::iterator it = m_OpenDevToolsURLs.find(browser->GetMainFrame()->GetURL()); if (it != m_OpenDevToolsURLs.end()) m_OpenDevToolsURLs.erase(it); } } void ClientHandler::OnLoadStart(CefRefPtr browser, CefRefPtr frame) { REQUIRE_UI_THREAD(); if (m_BrowserId == browser->GetIdentifier() && frame->IsMain()) { // We've just started loading a page SetLoading(true); } } void ClientHandler::OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) { REQUIRE_UI_THREAD(); if (m_BrowserId == browser->GetIdentifier() && frame->IsMain()) { // We've just finished loading a page SetLoading(false); // Continue the DOM test. if (frame->GetURL() == dom_test::kTestUrl) dom_test::OnLoadEnd(browser); } } void ClientHandler::OnLoadError(CefRefPtr browser, CefRefPtr frame, ErrorCode errorCode, const CefString& errorText, const CefString& failedUrl) { REQUIRE_UI_THREAD(); // Display a load error message. std::stringstream ss; ss << "

Failed to load URL " << std::string(failedUrl) << " with error " << std::string(errorText) << " (" << errorCode << ").

"; frame->LoadString(ss.str(), failedUrl); } CefRefPtr ClientHandler::GetResourceHandler( CefRefPtr browser, CefRefPtr frame, CefRefPtr request) { std::string url = request->GetURL(); if (url == "http://tests/request") { // Show the request contents std::string dump; DumpRequestContents(request, dump); CefRefPtr stream = CefStreamReader::CreateForData( static_cast(const_cast(dump.c_str())), dump.size()); ASSERT(stream.get()); return new CefStreamResourceHandler("text/plain", stream); } else if (url == "http://tests/dialogs") { // Show the dialogs contents CefRefPtr stream = GetBinaryResourceReader("dialogs.html"); ASSERT(stream.get()); return new CefStreamResourceHandler("text/html", stream); } else if (url == dom_test::kTestUrl) { // Show the domaccess contents CefRefPtr stream = GetBinaryResourceReader("domaccess.html"); ASSERT(stream.get()); return new CefStreamResourceHandler("text/html", stream); } else if (url == "http://tests/localstorage") { // Show the localstorage contents CefRefPtr stream = GetBinaryResourceReader("localstorage.html"); ASSERT(stream.get()); return new CefStreamResourceHandler("text/html", stream); } else if (url == "http://tests/xmlhttprequest") { // Show the xmlhttprequest contents CefRefPtr stream = GetBinaryResourceReader("xmlhttprequest.html"); ASSERT(stream.get()); return new CefStreamResourceHandler("text/html", stream); } CefRefPtr handler; // Execute delegate callbacks. RequestDelegateSet::iterator it = request_delegates_.begin(); for (; it != request_delegates_.end() && !handler.get(); ++it) handler = (*it)->GetResourceHandler(this, browser, frame, request); return handler; } void ClientHandler::OnLoadingStateChange(CefRefPtr browser, bool isLoading, bool canGoBack, bool canGoForward) { REQUIRE_UI_THREAD(); SetLoading(isLoading); SetNavState(canGoBack, canGoForward); } bool ClientHandler::OnConsoleMessage(CefRefPtr browser, const CefString& message, const CefString& source, int line) { REQUIRE_UI_THREAD(); bool first_message; std::string logFile; { AutoLock lock_scope(this); first_message = m_LogFile.empty(); if (first_message) { std::stringstream ss; ss << AppGetWorkingDirectory(); #if defined(OS_WIN) ss << "\\"; #else ss << "/"; #endif ss << "console.log"; m_LogFile = ss.str(); } logFile = m_LogFile; } FILE* file = fopen(logFile.c_str(), "a"); if (file) { std::stringstream ss; ss << "Message: " << std::string(message) << "\r\nSource: " << std::string(source) << "\r\nLine: " << line << "\r\n-----------------------\r\n"; fputs(ss.str().c_str(), file); fclose(file); if (first_message) SendNotification(NOTIFY_CONSOLE_MESSAGE); } return false; } void ClientHandler::OnRequestGeolocationPermission( CefRefPtr browser, const CefString& requesting_url, int request_id, CefRefPtr callback) { // Allow geolocation access from all websites. callback->Continue(true); } void ClientHandler::OnBeforeContextMenu( CefRefPtr browser, CefRefPtr frame, CefRefPtr params, CefRefPtr model) { if ((params->GetTypeFlags() & (CM_TYPEFLAG_PAGE | CM_TYPEFLAG_FRAME)) != 0) { // Add a separator if the menu already has items. if (model->GetCount() > 0) model->AddSeparator(); // Add a "Show DevTools" item to all context menus. model->AddItem(CLIENT_ID_SHOW_DEVTOOLS, "&Show DevTools"); CefString devtools_url = browser->GetHost()->GetDevToolsURL(true); if (devtools_url.empty() || m_OpenDevToolsURLs.find(devtools_url) != m_OpenDevToolsURLs.end()) { // Disable the menu option if DevTools isn't enabled or if a window is // already open for the current URL. model->SetEnabled(CLIENT_ID_SHOW_DEVTOOLS, false); } // Test context menu features. BuildTestMenu(model); } } bool ClientHandler::OnContextMenuCommand( CefRefPtr browser, CefRefPtr frame, CefRefPtr params, int command_id, EventFlags event_flags) { switch (command_id) { case CLIENT_ID_SHOW_DEVTOOLS: ShowDevTools(browser); return true; default: // Allow default handling, if any. return ExecuteTestMenu(command_id); } } void ClientHandler::SetMainHwnd(CefWindowHandle hwnd) { AutoLock lock_scope(this); m_MainHwnd = hwnd; } void ClientHandler::SetEditHwnd(CefWindowHandle hwnd) { AutoLock lock_scope(this); m_EditHwnd = hwnd; } void ClientHandler::SetButtonHwnds(CefWindowHandle backHwnd, CefWindowHandle forwardHwnd, CefWindowHandle reloadHwnd, CefWindowHandle stopHwnd) { AutoLock lock_scope(this); m_BackHwnd = backHwnd; m_ForwardHwnd = forwardHwnd; m_ReloadHwnd = reloadHwnd; m_StopHwnd = stopHwnd; } std::string ClientHandler::GetLogFile() { AutoLock lock_scope(this); return m_LogFile; } void ClientHandler::SetLastDownloadFile(const std::string& fileName) { AutoLock lock_scope(this); m_LastDownloadFile = fileName; } std::string ClientHandler::GetLastDownloadFile() { AutoLock lock_scope(this); return m_LastDownloadFile; } void ClientHandler::ShowDevTools(CefRefPtr browser) { std::string devtools_url = browser->GetHost()->GetDevToolsURL(true); if (!devtools_url.empty() && m_OpenDevToolsURLs.find(devtools_url) == m_OpenDevToolsURLs.end()) { m_OpenDevToolsURLs.insert(devtools_url); browser->GetMainFrame()->ExecuteJavaScript( "window.open('" + devtools_url + "');", "about:blank", 0); } } // static void ClientHandler::CreateProcessMessageDelegates( ProcessMessageDelegateSet& delegates) { // Create the binding test delegates. binding_test::CreateProcessMessageDelegates(delegates); } // static void ClientHandler::CreateRequestDelegates(RequestDelegateSet& delegates) { // Create the binding test delegates. binding_test::CreateRequestDelegates(delegates); } void ClientHandler::BuildTestMenu(CefRefPtr model) { if (model->GetCount() > 0) model->AddSeparator(); // Build the sub menu. CefRefPtr submenu = model->AddSubMenu(CLIENT_ID_TESTMENU_SUBMENU, "Context Menu Test"); submenu->AddCheckItem(CLIENT_ID_TESTMENU_CHECKITEM, "Check Item"); submenu->AddRadioItem(CLIENT_ID_TESTMENU_RADIOITEM1, "Radio Item 1", 0); submenu->AddRadioItem(CLIENT_ID_TESTMENU_RADIOITEM2, "Radio Item 2", 0); submenu->AddRadioItem(CLIENT_ID_TESTMENU_RADIOITEM3, "Radio Item 3", 0); // Check the check item. if (m_TestMenuState.check_item) submenu->SetChecked(CLIENT_ID_TESTMENU_CHECKITEM, true); // Check the selected radio item. submenu->SetChecked( CLIENT_ID_TESTMENU_RADIOITEM1 + m_TestMenuState.radio_item, true); } bool ClientHandler::ExecuteTestMenu(int command_id) { if (command_id == CLIENT_ID_TESTMENU_CHECKITEM) { // Toggle the check item. m_TestMenuState.check_item ^= 1; return true; } else if (command_id >= CLIENT_ID_TESTMENU_RADIOITEM1 && command_id <= CLIENT_ID_TESTMENU_RADIOITEM3) { // Store the selected radio item. m_TestMenuState.radio_item = (command_id - CLIENT_ID_TESTMENU_RADIOITEM1); return true; } // Allow default handling to proceed. return false; }