/* Copyright (C) 2015 LiveCode Ltd. This file is part of LiveCode. LiveCode is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License v3 as published by the Free Software Foundation. LiveCode is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with LiveCode. If not see . */ #include #include #include "libbrowser_cef.h" #include #include #include #include #include #if defined(TARGET_PLATFORM_POSIX) #include "signal_restore_posix.h" #include #endif //////////////////////////////////////////////////////////////////////////////// #define CEF_DUMMY_URL "http://libbrowser_dummy_url/" //////////////////////////////////////////////////////////////////////////////// // String conversion bool MCCefStringToUtf8String(const CefString &p_cef_string, char *&r_u8_string) { if (p_cef_string.empty()) return MCCStringClone("", r_u8_string); return MCCStringFromUnicode(p_cef_string.c_str(), r_u8_string); } bool MCCefStringFromUtf8String(const char *p_u8_string, cef_string_t *r_cef_string) { return 0 != cef_string_from_utf8(p_u8_string, MCCStringLength(p_u8_string), r_cef_string); } bool MCCefStringFromUtf8String(const char *p_u8_string, CefString &r_cef_string) { return MCCefStringFromUtf8String(p_u8_string, r_cef_string.GetWritableStruct()); } bool MCCefStringToUInt(const CefString &p_string, uint32_t &r_int) { char_t * t_tmp_string; t_tmp_string = nil; uint32_t t_int; bool t_success; t_success = MCCefStringToUtf8String(p_string, t_tmp_string); if (t_success) { char *t_end; t_int = strtoul(t_tmp_string, &t_end, 10); t_success = t_end == t_tmp_string + MCCStringLength(t_tmp_string); } if (t_tmp_string != nil) MCCStringFree(t_tmp_string); if (t_success) r_int = t_int; return t_success; } //////////////////////////////////////////////////////////////////////////////// bool MCCefListToBrowserList(CefRefPtr p_list, MCBrowserListRef &r_list) { bool t_success; t_success = true; MCBrowserListRef t_list; t_list = nil; size_t t_size; t_size = p_list->GetSize(); if (t_success) t_success = MCBrowserListCreate(t_list, t_size); for (uint32_t i = 0; t_success && i < t_size; i++) { switch (p_list->GetType(i)) { case VTYPE_BOOL: t_success = MCBrowserListSetBoolean(t_list, i, p_list->GetBool(i)); break; case VTYPE_INT: t_success = MCBrowserListSetInteger(t_list, i, p_list->GetInt(i)); break; case VTYPE_DOUBLE: t_success = MCBrowserListSetDouble(t_list, i, p_list->GetDouble(i)); break; case VTYPE_STRING: { char *t_string = nil; t_success = MCCefStringToUtf8String(p_list->GetString(i), t_string) && MCBrowserListSetUTF8String(t_list, i, t_string); if (t_string != nil) MCCStringFree(t_string); break; } case VTYPE_LIST: { MCBrowserListRef t_list_val = nil; t_success = MCCefListToBrowserList(p_list->GetList(i), t_list_val) && MCBrowserListSetList(t_list, i, t_list_val); MCBrowserListRelease(t_list_val); break; } case VTYPE_DICTIONARY: { MCBrowserDictionaryRef t_dict_val = nil; t_success = MCCefDictionaryToBrowserDictionary(p_list->GetDictionary(i), t_dict_val) && MCBrowserListSetDictionary(t_list, i, t_dict_val); MCBrowserDictionaryRelease(t_dict_val); break; } default: // unimplemented value type t_success = false; } } if (t_success) r_list = t_list; else MCBrowserListRelease(t_list); return t_success; } bool MCCefDictionaryToBrowserDictionary(CefRefPtr p_dict, MCBrowserDictionaryRef &r_dict) { bool t_success; t_success = true; MCBrowserDictionaryRef t_dict; t_dict = nil; CefDictionaryValue::KeyList t_key_list; if (t_success) t_success = p_dict->GetKeys(t_key_list); if (t_success) t_success = MCBrowserDictionaryCreate(t_dict, t_key_list.size()); for (CefDictionaryValue::KeyList::iterator i = t_key_list.begin(); t_success && i < t_key_list.end(); i++) { char *t_key; t_key = nil; if (t_success) t_success = MCCefStringToUtf8String(*i, t_key); if (t_success) { switch (p_dict->GetType(*i)) { case VTYPE_BOOL: t_success = MCBrowserDictionarySetBoolean(t_dict, t_key, p_dict->GetBool(*i)); break; case VTYPE_INT: t_success = MCBrowserDictionarySetInteger(t_dict, t_key, p_dict->GetInt(*i)); break; case VTYPE_DOUBLE: t_success = MCBrowserDictionarySetDouble(t_dict, t_key, p_dict->GetDouble(*i)); break; case VTYPE_STRING: { char *t_string = nil; t_success = MCCefStringToUtf8String(p_dict->GetString(*i), t_string) && MCBrowserDictionarySetUTF8String(t_dict, t_key, t_string); if (t_string != nil) MCCStringFree(t_string); break; } case VTYPE_LIST: { MCBrowserListRef t_list_val = nil; t_success = MCCefListToBrowserList(p_dict->GetList(*i), t_list_val) && MCBrowserDictionarySetList(t_dict, t_key, t_list_val); MCBrowserListRelease(t_list_val); break; } case VTYPE_DICTIONARY: { MCBrowserDictionaryRef t_dict_val = nil; t_success = MCCefDictionaryToBrowserDictionary(p_dict->GetDictionary(*i), t_dict_val) && MCBrowserDictionarySetDictionary(t_dict, t_key, t_dict_val); MCBrowserDictionaryRelease(t_dict_val); break; } default: // unimplemented value type t_success = false; } } if (t_key != nil) MCCStringFree(t_key); } if (t_success) r_dict = t_dict; else MCBrowserDictionaryRelease(t_dict); return t_success; } //////////////////////////////////////////////////////////////////////////////// static const char *s_auth_scheme_strings[] = { "basic", "digest", "ntlm", "negotiate", "spdyproxy", nil }; bool MCCefAuthSchemeFromCefString(const CefString &p_string, MCCefAuthScheme &r_scheme) { for (uint32_t i = 0; s_auth_scheme_strings[i] != nil; i++) { if (p_string == s_auth_scheme_strings[i]) { r_scheme = (MCCefAuthScheme)i; return true; } } return false; } //////////////////////////////////////////////////////////////////////////////// // Cef initialization / shutdown handling static bool s_cef_initialised = false; static bool s_cefbrowser_initialised = false; static uint32_t s_instance_count = 0; static CefScopedTempDir s_temp_cache; void MCCefBrowserExternalInit(void) { // set up static vars s_cef_initialised = false; s_cefbrowser_initialised = false; s_instance_count = 0; } void MCCefBrowserRunloopAction(void *p_context) { CefDoMessageLoopWork(); } class MCCefBrowserApp : public CefApp, CefBrowserProcessHandler { public: virtual CefRefPtr GetBrowserProcessHandler() OVERRIDE { return this; } virtual void OnBeforeChildProcessLaunch(CefRefPtr p_command_line) OVERRIDE { if (MCCefPlatformGetHiDPIEnabled()) p_command_line->AppendSwitch(MC_CEF_HIDPI_SWITCH); } virtual void OnBeforeCommandLineProcessing(const CefString &p_process_type, CefRefPtr p_command_line) OVERRIDE { // Enable WebRTC p_command_line->AppendSwitch("enable-media-stream"); } IMPLEMENT_REFCOUNTING(MCCefBrowserApp); }; extern "C" int initialise_weak_link_cef(void); // These come from the engine we link to. extern "C" void *MCSupportLibraryLoad(const char *p_path); extern "C" void *MCSupportLibraryUnload(void *p_handle); extern "C" char *MCSupportLibraryCopyNativePath(void *p_handle); #if defined(WIN32) static const char *kCefProcessName = "libbrowser-cefprocess.exe"; static const char *kCefPathSeparatorStr = "\\"; static const char kCefPathSeparator = '\\'; #else static const char *kCefProcessName = "libbrowser-cefprocess"; static const char *kCefPathSeparatorStr = "/"; static const char kCefPathSeparator = '/'; #endif #if defined(TARGET_PLATFORM_LINUX) #include #include #include //////////////////////////////////////////////////////////////////////////////// static bool get_link_size(const char *p_path, uint32_t &r_size) { if (p_path == nil) return false; struct stat t_stat; if (lstat(p_path, &t_stat) == -1) return false; r_size = t_stat.st_size; return true; } static bool get_link_path(const char *p_link, char *&r_path) { bool t_success; t_success = true; char *t_buffer; t_buffer = nil; uint32_t t_buffer_size; t_buffer_size = 0; uint32_t t_link_size; t_success = get_link_size(p_link, t_link_size); while (t_success && t_link_size + 1 > t_buffer_size) { t_buffer_size = t_link_size + 1; t_success = MCBrowserMemoryReallocate(t_buffer, t_buffer_size, t_buffer); if (t_success) { int32_t t_read; t_read = readlink(p_link, t_buffer, t_buffer_size); t_success = t_read >= 0; t_link_size = t_read; } } if (t_success) { t_buffer[t_link_size] = '\0'; r_path = t_buffer; } else MCBrowserMemoryDeallocate(t_buffer); return t_success; } static bool get_exe_path_from_proc_fs(char *&r_path) { return get_link_path("/proc/self/exe", r_path); } ////////// const char *__MCCefPlatformGetExecutableFolder(void) { static char *s_exe_path = nil; if (s_exe_path == nil) { bool t_success; t_success = get_exe_path_from_proc_fs(s_exe_path); if (t_success) { // remove library component from path uint32_t t_index; if (MCCStringLastIndexOf(s_exe_path, '/', t_index)) s_exe_path[t_index] = '\0'; } } return s_exe_path; } #endif static bool __MCCefGetLibraryPath(char*& r_path) { void *t_module = nullptr; if (t_module == nullptr) t_module = MCSupportLibraryLoad("./CEF/libcef"); /* TODO[Bug 19381] On Linux and Windows, the in-git-checkout * location of CEF is "./CEF" but once LiveCode is installed it's * in "./Externals/CEF". */ #if defined(WIN32) || defined(TARGET_PLATFORM_LINUX) if (t_module == nullptr) t_module = MCSupportLibraryLoad("./Externals/CEF/libcef"); #endif char *t_module_path = t_module != nullptr ? MCSupportLibraryCopyNativePath(t_module) : nullptr; MCSupportLibraryUnload(t_module); if (t_module_path == nullptr) { return false; } char *t_last_sep = strrchr(t_module_path, kCefPathSeparator); if (t_last_sep == nullptr) { free(t_module_path); return false; } *t_last_sep = '\0'; r_path = t_module_path; return true; } static bool __MCCefAppendPath(const char *p_base, const char *p_path, char *&r_path) { if (p_base == nil) return MCCStringClone(p_path, r_path); else if (MCCStringEndsWith(p_base, kCefPathSeparatorStr) || *p_path == '\0') return MCCStringFormat(r_path, "%s%s", p_base, p_path); else return MCCStringFormat(r_path, "%s%s%s", p_base, kCefPathSeparatorStr, p_path); } static bool __MCCefBuildPath(const char *p_library_path, const char *p_suffix, cef_string_t* r_string) { char *t_process_path = nullptr; if (!__MCCefAppendPath(p_library_path, p_suffix, t_process_path) || !MCCefStringFromUtf8String(t_process_path, r_string)) { free(t_process_path); return false; } return true; } // IM-2014-03-13: [[ revBrowserCEF ]] Initialisation of the CEF library bool MCCefInitialise(void) { if (s_cef_initialised) return true; //////// char *t_library_path = nullptr; if (!__MCCefGetLibraryPath(t_library_path)) return false; //////// CefMainArgs t_args; CefSettings t_settings; t_settings.multi_threaded_message_loop = MC_CEF_USE_MULTITHREADED_MESSAGELOOP; t_settings.command_line_args_disabled = true; t_settings.windowless_rendering_enabled = true; t_settings.no_sandbox = true; #ifdef _DEBUG t_settings.log_severity = LOGSEVERITY_VERBOSE; #else t_settings.log_severity = LOGSEVERITY_DISABLE; #endif bool t_success = true; #ifdef TARGET_PLATFORM_LINUX if (t_success) t_success = __MCCefBuildPath(__MCCefPlatformGetExecutableFolder(), kCefProcessName, &t_settings.browser_subprocess_path); #else if (t_success) t_success = __MCCefBuildPath(t_library_path, kCefProcessName, &t_settings.browser_subprocess_path); #endif if (t_success) t_success = __MCCefBuildPath(t_library_path, "locales", &t_settings.locales_dir_path); if (t_success) t_success = __MCCefBuildPath(t_library_path, "", &t_settings.resources_dir_path); if (t_success) t_success = s_temp_cache.CreateUniqueTempDir(); if (t_success) CefString(&t_settings.cache_path).FromString(s_temp_cache.GetPath()); CefRefPtr t_app = new (nothrow) MCCefBrowserApp(); if (t_success) t_success = -1 == CefExecuteProcess(t_args, t_app, nil); if (t_success) { #if defined(TARGET_PLATFORM_POSIX) struct itimerval t_old_timer, t_new_timer; memset(&t_new_timer, 0, sizeof(t_new_timer)); setitimer(ITIMER_REAL, &t_new_timer, &t_old_timer); BackupSignalHandlers(); #endif t_success = CefInitialize(t_args, t_settings, t_app, nil); #if defined(TARGET_PLATFORM_POSIX) RestoreSignalHandlers(); setitimer(ITIMER_REAL, &t_old_timer, nil); #endif } s_cef_initialised = t_success; free(t_library_path); return s_cef_initialised; } // IM-2014-03-13: [[ revBrowserCEF ]] Initialise CEF & install the runloop action bool MCCefBrowserInitialise(void) { if (s_cefbrowser_initialised) return true; bool t_success; t_success = true; if (t_success) { t_success = initialise_weak_link_cef(); } if (t_success) t_success = MCCefInitialise(); if (t_success && !MC_CEF_USE_MULTITHREADED_MESSAGELOOP) t_success = MCBrowserAddRunloopAction(MCCefBrowserRunloopAction, nil); s_cefbrowser_initialised = t_success; return s_cefbrowser_initialised; } // IM-2014-03-13: [[ revBrowserCEF ]] Shutdown the CEF library void MCCefFinalise(void) { if (!s_cef_initialised) return; if (s_temp_cache.IsValid()) { s_temp_cache.Delete(); } CefShutdown(); s_cef_initialised = false; } // IM-2014-03-13: [[ revBrowserCEF ]] Shut down the browser void MCCefBrowserFinalise(void) { if (!s_cefbrowser_initialised) return; if (!MC_CEF_USE_MULTITHREADED_MESSAGELOOP) MCBrowserRemoveRunloopAction(MCCefBrowserRunloopAction, nil); s_cefbrowser_initialised = false; } void MCCefIncrementInstanceCount(void) { if (s_instance_count == 0) /* UNCHECKED */ MCCefBrowserInitialise(); s_instance_count++; } void MCCefDecrementInstanceCount(void) { if (s_instance_count == 0) return; s_instance_count--; if (s_instance_count == 0) MCCefBrowserFinalise(); } //////////////////////////////////////////////////////////////////////////////// // Utility class to hold a response from a request to the render process class MCCefMessageResult { private: bool m_have_result; bool m_success; CefValueType m_result_type; CefString m_result_string; int m_result_int; public: void SetResult(bool p_success, const CefString &p_result) { if (HaveResult()) Clear(); m_success = p_success; m_result_string = p_result; m_result_type = VTYPE_STRING; m_have_result = true; } void SetResult(bool p_success, int p_result) { if (HaveResult()) Clear(); m_success = p_success; m_result_int = p_result; m_result_type = VTYPE_INT; m_have_result = true; } bool HaveResult() { return m_have_result; } bool GetResult(CefString &r_result) const { if (!m_success) return false; if (m_result_type == VTYPE_STRING) { r_result = m_result_string; return true; } // convert to string bool t_converted; t_converted = false; switch (m_result_type) { case VTYPE_INT: { char *t_tmp; t_tmp = nil; t_converted = MCCStringFormat(t_tmp, "%d", m_result_int) && MCCefStringFromUtf8String(t_tmp, r_result); if (t_tmp != nil) MCCStringFree(t_tmp); } break; case VTYPE_NULL: case VTYPE_BOOL: case VTYPE_DOUBLE: case VTYPE_STRING: case VTYPE_BINARY: case VTYPE_DICTIONARY: case VTYPE_LIST: case VTYPE_INVALID: /* UNIMPLEMENTED */ break; } return t_converted; } bool GetResult(int &r_result) const { if (!m_success) return false; if (m_result_type == VTYPE_INT) { r_result = m_result_int; return true; } // convert to int return false; } void Clear() { m_have_result = false; m_result_string.ClearAndFree(); } }; //////////////////////////////////////////////////////////////////////////////// // Browser client - receives callback messages from the browser struct MCCefErrorInfo { CefString url; CefString error_message; CefLoadHandler::ErrorCode error_code; }; class MCCefBrowserClient : public CefClient, CefLifeSpanHandler, CefRequestHandler, /* CefDownloadHandler ,*/ CefLoadHandler, CefContextMenuHandler, CefDragHandler { public: enum PageOrigin { kNone, kSetUrl, kSetSource, kBrowse, }; private: int m_browser_id; int m_popup_browser_id = 0; MCCefBrowserBase *m_owner; MCCefMessageResult m_message_result; std::map m_load_error_frames; // Describes where loading url requests came from std::map m_load_url_origins; CefString m_last_request_url; PageOrigin m_displayed_page_origin; // Error handling - we need to keep track of url that failed to load in a // frame so we can send the correct url in onLoadEnd() void AddLoadErrorFrame(int64_t p_id, const CefString &p_url, const CefString &p_error_msg, CefLoadHandler::ErrorCode p_error_code) { m_load_error_frames[p_id].url = p_url; m_load_error_frames[p_id].error_message = p_error_msg; m_load_error_frames[p_id].error_code = p_error_code; } bool FindLoadErrorFrame(int64_t p_id, MCCefErrorInfo &r_info, bool p_delete) { std::map::iterator t_iter; t_iter = m_load_error_frames.find(p_id); if (t_iter == m_load_error_frames.end()) return false; r_info = t_iter->second; if (p_delete) m_load_error_frames.erase(t_iter); return true; } bool RemoveLoadErrorFrame(int64_t p_id, CefString &r_error_url, CefString &r_error_msg, CefLoadHandler::ErrorCode &r_error_code) { MCCefErrorInfo t_info; if (!FindLoadErrorFrame(p_id, t_info, true)) return false; r_error_url = t_info.url; r_error_msg = t_info.error_message; r_error_code = t_info.error_code; return true; } bool FetchLoadErrorFrame(int64_t p_id, CefString &r_error_url, CefString &r_error_msg, CefLoadHandler::ErrorCode &r_error_code) { MCCefErrorInfo t_info; if (!FindLoadErrorFrame(p_id, t_info, false)) return false; r_error_url = t_info.url; r_error_msg = t_info.error_message; r_error_code = t_info.error_code; return true; } public: MCCefBrowserClient(MCCefBrowserBase *p_owner) { m_owner = p_owner; m_browser_id = 0; m_displayed_page_origin = PageOrigin::kNone; } // IM-2014-07-21: [[ Bug 12296 ]] Method to allow owner to notify client of its deletion void OnOwnerClosed(void) { m_owner = nil; } // Tell browser which callback interfaces we implement virtual CefRefPtr GetLifeSpanHandler() OVERRIDE { return this; } virtual CefRefPtr GetRequestHandler() OVERRIDE { return this; } // virtual CefRefPtr GetDownloadHandler() OVERRIDE { return this; } virtual CefRefPtr GetLoadHandler() OVERRIDE { return this; } virtual CefRefPtr GetContextMenuHandler() OVERRIDE { return this; } virtual CefRefPtr GetDragHandler() OVERRIDE { return this; } void AddLoadingUrl(const CefString &p_url, PageOrigin p_origin) { m_load_url_origins[p_url] = p_origin; } void RemoveLoadingUrl(const CefString &p_url) { m_load_url_origins.erase(p_url); } bool GetLoadingUrlOrigin(const CefString &p_url, PageOrigin &r_origin) { auto t_iter = m_load_url_origins.find(p_url); if (t_iter == m_load_url_origins.end()) return false; r_origin = t_iter->second; return true; } PageOrigin GetDisplayedPageOrigin() { return m_displayed_page_origin; } MCCefMessageResult &GetMessageResult() { return m_message_result; } virtual bool OnProcessMessageReceived(CefRefPtr p_browser, CefProcessId p_source_process, CefRefPtr p_message) OVERRIDE { CefString t_message_name; t_message_name = p_message->GetName(); if (t_message_name == MC_CEFMSG_RESULT) { // When we get a result message, the result is stored in the message result object CefRefPtr t_args; t_args = p_message->GetArgumentList(); switch (t_args->GetType(1)) { case VTYPE_STRING: m_message_result.SetResult(t_args->GetBool(0), t_args->GetString(1)); break; case VTYPE_INT: m_message_result.SetResult(t_args->GetBool(0), t_args->GetInt(1)); break; default: MCAssert(false); } MCBrowserRunloopBreakWait(); return true; } else if (t_message_name == MC_CEFMSG_JS_HANDLER) { // Handle JavaScript call to LiveCode handler CefRefPtr t_args; t_args = p_message->GetArgumentList(); char *t_handler; t_handler = nil; bool t_success; t_success = true; // IM-2014-07-21: [[ Bug 12296 ]] Don't proceed with callback if browser has been closed if (t_success) t_success = nil != m_owner; if (t_success) t_success = MCCefStringToUtf8String(t_args->GetString(0), t_handler); MCBrowserListRef t_param_list; t_param_list = nil; if (t_success) t_success = MCCefListToBrowserList(t_args->GetList(1), t_param_list); if (t_success) m_owner->OnJavaScriptCall(t_handler, t_param_list); if (t_handler) MCCStringFree(t_handler); if (t_param_list != nil) MCBrowserListRelease(t_param_list); return true; } return CefClient::OnProcessMessageReceived(p_browser, p_source_process, p_message); } // CefLifeSpanHandler interface // Called on UI thread virtual void OnAfterCreated(CefRefPtr p_browser) OVERRIDE { // Keep track of first created browser instance. Any others belong to pop-ups if (m_browser_id == 0) { m_browser_id = p_browser->GetIdentifier(); // IM-2014-07-21: [[ Bug 12296 ]] Check owner has not been closed before invoking callback if (m_owner != nil) m_owner->OnCefBrowserCreated(p_browser); } else { m_popup_browser_id = p_browser->GetIdentifier(); } MCCefIncrementInstanceCount(); } virtual void OnBeforeClose(CefRefPtr p_browser) OVERRIDE { if (m_owner != nil) m_owner->OnCefBrowserClosed(p_browser); MCCefDecrementInstanceCount(); } virtual bool OnBeforePopup( CefRefPtr p_browser, CefRefPtr p_frame, const CefString& p_target_url, const CefString& p_target_frame_name, CefLifeSpanHandler::WindowOpenDisposition p_target_disposition, bool p_user_gesture, const CefPopupFeatures& p_popup_features, CefWindowInfo& p_window_info, CefRefPtr& p_client, CefBrowserSettings& p_settings, bool* p_no_javascript_access ) OVERRIDE { // returning true here will prevent popup windows from opening // IM-2014-07-21: [[ Bug 12296 ]] If browser has been closed then exit if (nil == m_owner) return true; CefWindowInfo(t_window_info); t_window_info.SetAsWindowless(p_browser->GetHost()->GetWindowHandle()); p_window_info = t_window_info; return false; } // CefDragHandler interface // Called on UI thread virtual bool OnDragEnter(CefRefPtr p_browser, CefRefPtr p_drag_data, CefDragHandler::DragOperationsMask p_mask) { // cancel the drag event return true; } // CefRequestHandler interface // Called on UI thread virtual bool OnBeforeBrowse(CefRefPtr p_browser, CefRefPtr p_frame, CefRefPtr p_request, bool p_user_gesture, bool p_is_redirect) OVERRIDE { // IM-2014-07-21: [[ Bug 12296 ]] If browser has been closed then exit if (nil == m_owner) return true; if (p_browser->GetIdentifier() == m_popup_browser_id) { char * t_url = nullptr; if (MCCefStringToUtf8String(p_request->GetURL(), t_url)) { m_owner->GoToURL(t_url); } p_browser->GetHost()->CloseBrowser(true); return false; } bool t_cancel; t_cancel = false; CefString t_url; t_url = p_request->GetURL(); PageOrigin t_origin = PageOrigin::kNone; if (p_user_gesture) { t_origin = PageOrigin::kBrowse; /* UNCHECKED */ AddLoadingUrl(t_url, t_origin); } else /* UNCHECKED */ GetLoadingUrlOrigin(t_url, t_origin); if (t_origin == PageOrigin::kSetSource) return false; char *t_url_str; t_url_str = nil; /* UNCHECKED */ MCCefStringToUtf8String(t_url, t_url_str); if (t_url_str != nil) MCCStringFree(t_url_str); return t_cancel; } virtual CefRequestHandler::ReturnValue OnBeforeResourceLoad(CefRefPtr p_browser, CefRefPtr p_frame, CefRefPtr p_request, CefRefPtr p_callback) OVERRIDE { // IM-2014-07-21: [[ Bug 12296 ]] If browser has been closed then exit if (nil == m_owner) return RV_CANCEL; CefString t_url; t_url = p_request->GetURL(); PageOrigin t_origin = PageOrigin::kNone; /* UNCHECKED */ GetLoadingUrlOrigin(t_url, t_origin); if (t_origin == PageOrigin::kSetSource) return RV_CONTINUE; m_last_request_url = t_url; CefString t_user_agent; if (!m_owner->GetUserAgent(t_user_agent)) return RV_CONTINUE; // modify request headers to set user agent CefRequest::HeaderMap t_headers; p_request->GetHeaderMap(t_headers); CefString t_key = "User-Agent"; // clear any current value t_headers.erase(t_key); // add new value t_headers.insert(std::pair(t_key, t_user_agent)); p_request->SetHeaderMap(t_headers); return RV_CONTINUE; } // IM-2014-04-28: [[ CefBrowser ]] Use platform-specific method to get credentials when requested virtual bool GetAuthCredentials( CefRefPtr p_browser, CefRefPtr p_frame, bool p_is_proxy, const CefString &p_host, int p_port, const CefString &p_realm, const CefString &p_method, CefRefPtr p_callback) OVERRIDE { // IM-2014-07-21: [[ Bug 12296 ]] If browser has been closed then exit if (nil == m_owner) return true; bool t_success; t_success = true; CefString t_user, t_pass; MCCefAuthScheme t_auth; if (t_success) t_success = MCCefAuthSchemeFromCefString(p_method, t_auth); if (t_success) t_success = m_owner->PlatformGetAuthCredentials(p_is_proxy, m_last_request_url, p_realm, t_auth, t_user, t_pass); if (t_success) t_success = !t_user.empty() && !t_pass.empty(); if (t_success) p_callback->Continue(t_user, t_pass); return t_success; } // CefDownloadHandler interface // Methods called on UI thread #if CEF_ON_DOWNLOAD_CALLBACK // TODO - Implement OnDownload callback virtual void OnBeforeDownload(CefRefPtr p_browser, CefRefPtr p_item, const CefString & p_suggested_name, CefRefPtr p_callback) OVERRIDE { // IM-2014-07-21: [[ Bug 12296 ]] If browser has been closed then exit if (nil == m_owner) return; bool t_cancel; t_cancel = false; CefString t_url; t_url = p_item->GetURL(); char *t_url_str; t_url_str = nil; /* UNCHECKED */ MCCefStringToUtf8String(t_url, t_url_str); CB_DownloadRequest(m_owner->GetInst(), t_url_str, &t_cancel); if (t_url_str != nil) MCCStringFree(t_url_str); CefString t_download_path; if (!t_cancel) p_callback->Continue(t_download_path, false); } #endif // CefLoadHandler interface // Methods called on UI thread or render process main thread virtual void OnLoadStart(CefRefPtr p_browser, CefRefPtr p_frame, cef_transition_type_t p_transition_type) OVERRIDE { // IM-2014-07-21: [[ Bug 12296 ]] If browser has been closed then exit if (nil == m_owner) return; // render process may have restarted so resend js handler list m_owner->SyncJavaScriptHandlers(); CefString t_url, t_error; CefLoadHandler::ErrorCode t_error_code; bool t_is_error; t_is_error = FetchLoadErrorFrame(p_frame->GetIdentifier(), t_url, t_error, t_error_code); if (!t_is_error) t_url = p_frame->GetURL(); bool t_frame; t_frame = !p_frame->IsMain(); PageOrigin t_origin = PageOrigin::kNone; /* UNCHECKED */ GetLoadingUrlOrigin(t_url, t_origin); if (!t_frame && !t_is_error) m_displayed_page_origin = t_origin; if (!t_frame && t_origin == PageOrigin::kSetSource) return; char *t_url_str; t_url_str = nil; /* UNCHECKED */ MCCefStringToUtf8String(t_url, t_url_str); if (!t_is_error) { if (!t_frame) m_owner->OnNavigationBegin(t_frame, t_url_str); m_owner->OnDocumentLoadBegin(t_frame, t_url_str); } if (t_url_str != nil) MCCStringFree(t_url_str); } virtual void OnLoadEnd(CefRefPtr p_browser, CefRefPtr p_frame, int p_http_status_code) OVERRIDE { // IM-2014-07-21: [[ Bug 12296 ]] If browser has been closed then exit if (nil == m_owner) return; CefString t_url, t_error; CefLoadHandler::ErrorCode t_error_code; bool t_is_error; t_is_error = RemoveLoadErrorFrame(p_frame->GetIdentifier(), t_url, t_error, t_error_code); if (!t_is_error) t_url = p_frame->GetURL(); bool t_frame; t_frame = !p_frame->IsMain(); PageOrigin t_origin = PageOrigin::kNone; /* UNCHECKED */ GetLoadingUrlOrigin(t_url, t_origin); /* UNCHECKED */ RemoveLoadingUrl(t_url); if (!t_frame && t_origin == PageOrigin::kSetSource) return CefLoadHandler::OnLoadEnd(p_browser, p_frame, p_http_status_code); char *t_url_str; t_url_str = nil; /* UNCHECKED */ MCCefStringToUtf8String(t_url, t_url_str); if (t_is_error) { if (t_error_code != ERR_UNKNOWN_URL_SCHEME) { char *t_err_str; t_err_str = nil; /* UNCHECKED */ MCCefStringToUtf8String(t_error, t_err_str); m_owner->OnDocumentLoadFailed(t_frame, t_url_str, t_err_str); if (!t_frame) m_owner->OnNavigationFailed(t_frame, t_url_str, t_err_str); if (t_err_str != nil) MCCStringFree(t_err_str); } } else { m_owner->OnDocumentLoadComplete(t_frame, t_url_str); if (!t_frame) m_owner->OnNavigationComplete(t_frame, t_url_str); } if (t_url_str != nil) MCCStringFree(t_url_str); } virtual void OnLoadError(CefRefPtr p_browser, CefRefPtr p_frame, CefLoadHandler::ErrorCode p_error_code, const CefString &p_error_text, const CefString &p_failed_url) OVERRIDE { if (p_error_code == ERR_UNKNOWN_URL_SCHEME) { bool t_frame; t_frame = !p_frame->IsMain(); char *t_url_str = nullptr; if (MCCefStringToUtf8String(p_failed_url, t_url_str)) { m_owner->OnNavigationRequestUnhandled(t_frame, t_url_str); } } else if (p_error_code != ERR_ABORTED) { // IM-2015-11-16: [[ Bug 16360 ]] Contrary to the CEF API docs, OnLoadEnd is NOT called after OnLoadError when the error code is ERR_ABORTED. // This occurs when requesting a new url be loaded when in the middle of loading the previous url, or when the url load is otherwise cancelled. AddLoadErrorFrame(p_frame->GetIdentifier(), p_failed_url, p_error_text, p_error_code); } } // ContextMenuHandler interface // Methods called on UI thread virtual void OnBeforeContextMenu(CefRefPtr p_browser, CefRefPtr p_frame, CefRefPtr p_params, CefRefPtr p_model) OVERRIDE { // IM-2014-07-21: [[ Bug 12296 ]] If browser has been closed then exit if (nil == m_owner) return; extern void MCCefMenuFilterItems(CefRefPtr p_menu); if (m_owner->GetEnableContextMenu()) MCCefMenuFilterItems(p_model); else p_model->Clear(); } IMPLEMENT_REFCOUNTING(MCCefBrowserClient); }; void MCCefMenuFilterItems(CefRefPtr p_menu) { // Remove unwanted items from the default menu, // keeping text editing and autocorrect options cef_menu_item_type_t t_prev_type = MENUITEMTYPE_NONE; uindex_t t_item_count = p_menu->GetCount(); uindex_t t_index = 0; while (t_index < t_item_count) { bool t_remove = false; cef_menu_item_type_t t_type = p_menu->GetTypeAt(t_index); if (t_type == MENUITEMTYPE_SUBMENU) { CefRefPtr t_submenu; t_submenu = p_menu->GetSubMenuAt(t_index); MCCefMenuFilterItems(t_submenu); // remove filtered submenu if empty t_remove = t_submenu->GetCount() == 0; } else if (t_type == MENUITEMTYPE_SEPARATOR) { // remove separator if last item in menu was a separator if (t_prev_type == MENUITEMTYPE_SEPARATOR) t_remove = true; } else { int t_command_id; t_command_id = p_menu->GetCommandIdAt(t_index); switch (t_command_id) { case MENU_ID_UNDO: case MENU_ID_REDO: case MENU_ID_CUT: case MENU_ID_COPY: case MENU_ID_PASTE: case MENU_ID_DELETE: case MENU_ID_SELECT_ALL: break; case MENU_ID_SPELLCHECK_SUGGESTION_0: case MENU_ID_SPELLCHECK_SUGGESTION_1: case MENU_ID_SPELLCHECK_SUGGESTION_2: case MENU_ID_SPELLCHECK_SUGGESTION_3: case MENU_ID_SPELLCHECK_SUGGESTION_4: case MENU_ID_NO_SPELLING_SUGGESTIONS: case MENU_ID_ADD_TO_DICTIONARY: break; default: t_remove = true; break; } } if (t_remove) { p_menu->RemoveAt(t_index); t_item_count--; } else { // remember the type of this item t_prev_type = t_type; t_index++; } } // if the last item is a separator then remove it if (t_prev_type == MENUITEMTYPE_SEPARATOR) { p_menu->RemoveAt(t_item_count - 1); } } bool MCCefBrowserBase::Initialize() { // create client and browser m_client = new (nothrow) MCCefBrowserClient(this); CefWindowInfo t_window_info; CefBrowserSettings t_settings; // IM-2014-05-06: [[ Bug 12384 ]] Browser must be created with non-empty URL or setting // htmltext will not work // Using a blank data url here forces the CEF render process to load, // allowing subsequent calls to MCCefBrowserBase::LoadHTMLText to succeed CefString t_url("data:text/html;charset=utf-8,"); // IM-2014-05-06: [[ Bug 12384 ]] Prevent callback messages for dummy URL m_client->AddLoadingUrl(t_url, MCCefBrowserClient::PageOrigin::kSetSource); PlatformConfigureWindow(t_window_info); if (MC_CEF_USE_MULTITHREADED_MESSAGELOOP) { // need to use asnyc version when not on UI thread if (!CefBrowserHost::CreateBrowser(t_window_info, m_client.get(), t_url, t_settings, NULL)) return false; while (m_browser == nil) MCBrowserRunloopWait(); } else CefBrowserHost::CreateBrowserSync(t_window_info, m_client.get(), t_url, t_settings, NULL); return m_browser != nil; } void MCCefBrowserBase::Finalize() { if (m_browser != nil) m_browser->GetHost()->CloseBrowser(true); // IM-2014-07-21: [[ Bug 12296 ]] Notify client of browser being closed if (m_client) m_client->OnOwnerClosed(); if (m_javascript_handlers != nil) MCCStringFree(m_javascript_handlers); m_browser = nil; m_client = nil; } MCCefBrowserBase::MCCefBrowserBase() { MCCefIncrementInstanceCount(); m_browser = nil; m_client = nil; m_instance_id = 0; m_send_advanced_messages = false; m_show_context_menu = true; m_allow_new_window = false; m_javascript_handlers = nil; m_js_handler_list = CefListValue::Create(); } MCCefBrowserBase::~MCCefBrowserBase(void) { MCCefDecrementInstanceCount(); } void MCCefBrowserBase::Destroy() { // Calling virtual methods from the destructor is A Bad Thing, so make sure // we close the CEF browser (which results in a call to // virtual method PlatformCloseBrowserWindow()) before destruction. Finalize(); MCBrowserRefCounted::Destroy(); } //////////////////////////////////////////////////////////////////////////////// MCBrowser *MCCefBrowserInstantiate(void *p_display, void *p_parent_window) { // IM-2014-03-18: [[ revBrowserCEF ]] Make sure cef library is loaded before trying to create browser if (!MCCefBrowserInitialise()) return nil; MCCefBrowserBase *t_browser; if (!MCCefPlatformCreateBrowser(p_display, p_parent_window, t_browser)) return nil; if (!t_browser->Initialize()) { delete t_browser; return nil; } return t_browser; } //////////////////////////////////////////////////////////////////////////////// void MCCefBrowserBase::OnCefBrowserCreated(CefRefPtr p_browser) { if (m_browser == nil) { m_browser = p_browser; // Make sure we break out of the wait loop if created asynchronously if (MC_CEF_USE_MULTITHREADED_MESSAGELOOP) MCBrowserRunloopBreakWait(); } } void MCCefBrowserBase::OnCefBrowserClosed(CefRefPtr p_browser) { } //////////////////////////////////////////////////////////////////////////////// CefRefPtr MCCefBrowserBase::GetCefBrowser(void) { return m_browser; } bool MCCefBrowserBase::GetUserAgent(CefString &r_user_agent) { if (m_user_agent.empty()) return false; r_user_agent = m_user_agent; return true; } //////////////////////////////////////////////////////////////////////////////// // wait for result from async request void MCCefBrowserBase::WaitOnResult() { MCCefMessageResult &t_result = m_client->GetMessageResult(); bool t_success; t_success = true; while (t_success && !t_result.HaveResult()) t_success = MCBrowserRunloopWait(); } bool MCCefBrowserBase::WaitOnResultString(CefString &r_result) { WaitOnResult(); return m_client->GetMessageResult().GetResult(r_result); } // send the process message and wait for the result bool MCCefBrowserBase::GetMessageResult(CefProcessId p_target, CefRefPtr p_message, const MCCefMessageResult *&r_result) { bool t_success; t_success = true; MCCefMessageResult &t_result = m_client->GetMessageResult(); t_result.Clear(); if (t_success) t_success = m_browser->SendProcessMessage(p_target, p_message); if (t_success) { WaitOnResult(); r_result = &t_result; } return t_success; } bool MCCefBrowserBase::GetMessageResultString(CefProcessId p_target, CefRefPtr p_message, CefString &r_result) { bool t_success; t_success = true; const MCCefMessageResult *t_result; t_result = nil; t_success = GetMessageResult(p_target, p_message, t_result); if (t_success) t_success = t_result->GetResult(r_result); return t_success; } // evaluate the javascript and return the result bool MCCefBrowserBase::EvalJavaScript(const CefString &p_script, const MCCefMessageResult *&r_result) { bool t_success; t_success = true; CefRefPtr t_message; t_message = CefProcessMessage::Create(MC_CEFMSG_EXECUTE_SCRIPT); t_success = t_message != nil; if (t_success) { CefRefPtr t_args; t_args = t_message->GetArgumentList(); t_success = t_args->SetString(0, p_script); } if (t_success) t_success = GetMessageResult(PID_RENDERER, t_message, r_result); return t_success; } bool MCCefBrowserBase::EvalJavaScript(const CefString &p_script, CefString &r_value) { bool t_success; t_success = true; const MCCefMessageResult *t_result; t_result = nil; t_success = EvalJavaScript(p_script, t_result); if (t_success) t_success = t_result->GetResult(r_value); return t_success; } bool MCCefBrowserBase::EvalJavaScript(const CefString &p_script, int &r_value) { bool t_success; t_success = true; const MCCefMessageResult *t_result; t_result = nil; t_success = EvalJavaScript(p_script, t_result); if (t_success) t_success = t_result->GetResult(r_value); return t_success; } //////////////////////////////////////////////////////////////////////////////// // Browser Properties bool MCCefBrowserBase::GetEnableContextMenu(void) { return m_show_context_menu; } void MCCefBrowserBase::SetEnableContextMenu(bool p_menu) { m_show_context_menu = p_menu; } bool MCCefBrowserBase::GetAllowNewWindow(void) { return m_allow_new_window; } void MCCefBrowserBase::SetAllowNewWindow(bool p_new_window) { m_allow_new_window = p_new_window; } class MCStringVisitor : public CefStringVisitor { private: MCCefMessageResult *m_result; public: MCStringVisitor(MCCefMessageResult &p_result) { m_result = &p_result; } void Visit(const CefString &p_string) OVERRIDE { m_result->SetResult(true, p_string); } IMPLEMENT_REFCOUNTING(MCStringVisitor); }; char *MCCefBrowserBase::GetSource(void) { MCCefMessageResult &t_result = m_client->GetMessageResult(); t_result.Clear(); CefRefPtr t_visitor; t_visitor = new (nothrow) MCStringVisitor(t_result); m_browser->GetMainFrame()->GetSource(t_visitor); bool t_success; t_success = true; CefString t_src; t_success = WaitOnResultString(t_src); char *t_src_str; t_src_str = nil; if (t_success) t_success = MCCefStringToUtf8String(t_src, t_src_str); return t_src_str; } bool MCCefBrowserBase::LoadHTMLText(const char *p_htmltext, const char *p_base_url) { // IM-2014-06-25: [[ Bug 12701 ]] CEF will crash if given an empty source string, // so replace here with the source of an empty page :) if (p_htmltext == nil || MCCStringLength(p_htmltext) == 0) p_htmltext = ""; CefString t_htmltext; if (!MCCefStringFromUtf8String(p_htmltext, t_htmltext)) return false; CefString t_base_url; if (!MCCefStringFromUtf8String(p_base_url, t_base_url)) return false; m_client->AddLoadingUrl(t_base_url, MCCefBrowserClient::PageOrigin::kSetSource); m_browser->GetMainFrame()->LoadString(t_htmltext, t_base_url); return true; } void MCCefBrowserBase::SetSource(const char *p_source) { /* UNCHECKED */ LoadHTMLText(p_source, CEF_DUMMY_URL); } #define MCCEF_VERTICAL_OVERFLOW_PROPERTY "document.body.style.overflowY" #define MCCEF_HORIZONTAL_OVERFLOW_PROPERTY "document.body.style.overflowX" inline const char *scrollbar_property(MCCefScrollbarDirection p_direction) { return p_direction == kMCCefScrollbarVertical ? MCCEF_VERTICAL_OVERFLOW_PROPERTY : MCCEF_HORIZONTAL_OVERFLOW_PROPERTY; } // IM-2014-08-25: [[ Bug 13272 ]] Implement CEF browser scrollbar property. bool MCCefBrowserBase::GetOverflowHidden(MCCefScrollbarDirection p_direction) { // property available through JavaScript bool t_success; t_success = true; CefString t_value; t_success = EvalJavaScript(scrollbar_property(p_direction), t_value); // assume scrollbars are visible if property fetch fails if (!t_success) return true; return t_value == L"hidden"; } // IM-2014-08-25: [[ Bug 13272 ]] Implement CEF browser scrollbar property. void MCCefBrowserBase::SetOverflowHidden(MCCefScrollbarDirection p_direction, bool p_hidden) { // property available through JavaScript bool t_success; t_success = true; char *t_overflow_script; t_overflow_script = nil; t_success = MCCStringFormat(t_overflow_script, "%s = \"%s\"", scrollbar_property(p_direction), p_hidden ? "hidden" : ""); CefString t_return_value; if (t_success) t_success = EvalJavaScript(t_overflow_script, t_return_value); MCCStringFree(t_overflow_script); } bool MCCefBrowserBase::GetVerticalScrollbarEnabled(void) { // IM-2014-08-25: [[ Bug 13272 ]] Show / hide scrollbars by setting the overflow style to empty / "hidden". return !GetOverflowHidden(kMCCefScrollbarVertical); } void MCCefBrowserBase::SetVerticalScrollbarEnabled(bool p_scrollbars) { // IM-2014-08-25: [[ Bug 13272 ]] Show / hide scrollbars by setting the overflow style to empty / "hidden". /* UNCHECKED */ SetOverflowHidden(kMCCefScrollbarVertical, !p_scrollbars); } bool MCCefBrowserBase::GetHorizontalScrollbarEnabled(void) { // IM-2014-08-25: [[ Bug 13272 ]] Show / hide scrollbars by setting the overflow style to empty / "hidden". return !GetOverflowHidden(kMCCefScrollbarHorizontal); } void MCCefBrowserBase::SetHorizontalScrollbarEnabled(bool p_scrollbars) { // IM-2014-08-25: [[ Bug 13272 ]] Show / hide scrollbars by setting the overflow style to empty / "hidden". /* UNCHECKED */ SetOverflowHidden(kMCCefScrollbarHorizontal, !p_scrollbars); } bool MCCefBrowserBase::GetRect(MCBrowserRect &r_rect) { return PlatformGetRect(r_rect); } bool MCCefBrowserBase::SetRect(const MCBrowserRect &p_rect) { return PlatformSetRect(p_rect); } char *MCCefBrowserBase::GetUserAgent(void) { char *t_string; t_string = nil; /* UNCHECKED */ MCCefStringToUtf8String(m_user_agent, t_string); return t_string; } void MCCefBrowserBase::SetUserAgent(const char *p_user_agent) { /* UNCHECKED */ MCCefStringFromUtf8String(p_user_agent, m_user_agent); } char *MCCefBrowserBase::GetURL(void) { if (m_client->GetDisplayedPageOrigin() == MCCefBrowserClient::PageOrigin::kSetSource) { // return empty string if page was loaded from source char *t_url_string; t_url_string = nil; /* UNCHECKED */ MCCStringClone("", t_url_string); return t_url_string; } CefString t_url; t_url = m_browser->GetMainFrame()->GetURL(); char *t_url_str; t_url_str = nil; /* UNCHECKED */ MCCefStringToUtf8String(t_url, t_url_str); return t_url_str; } bool MCCefBrowserBase::GetImage(void*& r_data, int& r_length) { /* TODO - IMPLEMENT */ return false; } bool MCCefBrowserBase::GetIsSecure(void) { CefRefPtr t_browser = GetCefBrowser(); if (t_browser == nil) return false; CefRefPtr t_host = t_browser->GetHost(); if (t_host == nil) return false; CefRefPtr t_navigation_entry = t_host->GetVisibleNavigationEntry(); if (t_navigation_entry == nil) return false; CefRefPtr t_ssl_status = t_navigation_entry->GetSSLStatus(); if (t_ssl_status == nil) return false; return t_ssl_status->IsSecureConnection(); } bool MCCefBrowserBase::GetAllowUserInteraction(void) { bool t_value; /* UNCHECKED */ PlatformGetAllowUserInteraction(t_value); return t_value; } void MCCefBrowserBase::SetAllowUserInteraction(bool p_value) { /* UNCHECKED */ PlatformSetAllowUserInteraction(p_value); } //////////////////////////////////////////////////////////////////////////////// // Browser Actions bool MCCefBrowserBase::EvaluateJavaScript(const char *p_script, char *&r_result) { bool t_success; t_success = true; CefString t_script; t_success = MCCefStringFromUtf8String(p_script, t_script); CefString t_return_value; if (t_success) t_success = EvalJavaScript(t_script, t_return_value); char *t_return_value_str; t_return_value_str = nil; if (t_success) t_success = MCCefStringToUtf8String(t_return_value, t_return_value_str); if (t_success) r_result = t_return_value_str; else MCCStringFree(t_return_value_str); return t_success; } bool MCCefBrowserBase::GoToURL(const char *p_url) { CefRefPtr t_browser = GetCefBrowser(); if (t_browser == nil) return false; CefRefPtr t_frame; t_frame = t_browser->GetMainFrame(); if (t_frame == nil) return false; CefString t_url; if (!MCCefStringFromUtf8String(p_url, t_url)) return false; m_client->AddLoadingUrl(t_url, MCCefBrowserClient::PageOrigin::kSetUrl); t_frame->LoadURL(t_url); return true; } bool MCCefBrowserBase::StopLoading(void) { m_browser->StopLoad(); return true; } bool MCCefBrowserBase::Reload(void) { m_browser->Reload(); return true; } bool MCCefBrowserBase::GoBack(void) { m_browser->GoBack(); return true; } bool MCCefBrowserBase::GoForward(void) { m_browser->GoForward(); return true; } bool MCCefBrowserBase::SyncJavaScriptHandlers() { CefRefPtr t_message; t_message = CefProcessMessage::Create(MC_CEFMSG_SET_JS_HANDLER_LIST); CefRefPtr t_args; t_args = t_message->GetArgumentList(); t_args->SetList(0, m_js_handler_list->Copy()); m_browser->SendProcessMessage(PID_RENDERER, t_message); /* UNCHECKED */ return true; } bool MCCefBrowserBase::SetJavaScriptHandlers(const char *p_handlers) { bool t_success; t_success = true; char *t_new_handlers; t_new_handlers = nil; if (t_success) t_success = MCCStringClone(p_handlers, t_new_handlers); char **t_handlers; t_handlers = nil; uint32_t t_handler_count; t_handler_count = 0; CefRefPtr t_handler_list; if (t_success) t_success = nil != (t_handler_list = CefListValue::Create()); if (!MCCStringIsEmpty(t_new_handlers)) { if (t_success) t_success = MCCStringSplit(t_new_handlers, '\n', t_handlers, t_handler_count); if (t_success) t_success = t_handler_list->SetSize(t_handler_count); for (uint32_t i = 0; t_success && i < t_handler_count; i++) { CefString t_string; t_success = MCCefStringFromUtf8String(t_handlers[i], t_string); if (t_success) t_success = t_handler_list->SetString(i, t_string); } } if (t_success) { m_js_handler_list = t_handler_list; /* UNCHECKED */ SyncJavaScriptHandlers(); } if (t_success) { if (m_javascript_handlers != nil) MCCStringFree(m_javascript_handlers); m_javascript_handlers = t_new_handlers; t_new_handlers = nil; } if (t_handlers != nil) MCCStringArrayFree(t_handlers, t_handler_count); if (t_new_handlers != nil) MCCStringFree(t_new_handlers); return t_success; } bool MCCefBrowserBase::GetJavaScriptHandlers(char *&r_handlers) { if (m_javascript_handlers == nil) return MCCStringClone("", r_handlers); else return MCCStringClone(m_javascript_handlers, r_handlers); } //////////////////////////////////////////////////////////////////////////////// void *MCCefBrowserBase::GetNativeLayer() { void *t_layer; if (!PlatformGetNativeLayer(t_layer)) return nil; return t_layer; } bool MCCefBrowserBase::GetBoolProperty(MCBrowserProperty p_property, bool &r_value) { switch (p_property) { case kMCBrowserAllowNewWindows: r_value = GetAllowNewWindow(); return true; case kMCBrowserEnableContextMenu: r_value = GetEnableContextMenu(); return true; case kMCBrowserVerticalScrollbarEnabled: r_value = GetVerticalScrollbarEnabled(); return true; case kMCBrowserHorizontalScrollbarEnabled: r_value = GetHorizontalScrollbarEnabled(); return true; case kMCBrowserIsSecure: r_value = GetIsSecure(); return true; case kMCBrowserAllowUserInteraction: r_value = GetAllowUserInteraction(); return true; default: break; } return true; } bool MCCefBrowserBase::SetBoolProperty(MCBrowserProperty p_property, bool p_value) { switch (p_property) { case kMCBrowserAllowNewWindows: SetAllowNewWindow(p_value); return true; case kMCBrowserEnableContextMenu: SetEnableContextMenu(p_value); return true; case kMCBrowserVerticalScrollbarEnabled: SetVerticalScrollbarEnabled(p_value); return true; case kMCBrowserHorizontalScrollbarEnabled: SetHorizontalScrollbarEnabled(p_value); return true; case kMCBrowserAllowUserInteraction: SetAllowUserInteraction(p_value); return true; default: break; } return true; } bool MCCefBrowserBase::SetStringProperty(MCBrowserProperty p_property, const char *p_value) { switch (p_property) { case kMCBrowserHTMLText: SetSource(p_value); return true; case kMCBrowserJavaScriptHandlers: return SetJavaScriptHandlers(p_value); case kMCBrowserUserAgent: SetUserAgent(p_value); return true; default: break; } return true; } bool MCCefBrowserBase::GetStringProperty(MCBrowserProperty p_property, char *&r_value) { switch (p_property) { case kMCBrowserHTMLText: r_value = GetSource(); return true; case kMCBrowserJavaScriptHandlers: return GetJavaScriptHandlers(r_value); case kMCBrowserUserAgent: r_value = GetUserAgent(); return true; case kMCBrowserURL: r_value = GetURL(); return true; default: break; } return true; } bool MCCefBrowserBase::SetIntegerProperty(MCBrowserProperty p_property, int32_t p_value) { switch (p_property) { default: break; } return true; } bool MCCefBrowserBase::GetIntegerProperty(MCBrowserProperty p_property, int32_t &r_value) { switch (p_property) { default: break; } return true; } //////////////////////////////////////////////////////////////////////////////// // Cef browser factory MCCefBrowserFactory::MCCefBrowserFactory() { MCCefBrowserExternalInit(); } MCCefBrowserFactory::~MCCefBrowserFactory() { // IM-2016-03-10: [[ Bug 17029 ]] Shutdown CEF library when factory deleted Finalize(); } bool MCCefBrowserFactory::Initialize() { return MCCefBrowserInitialise(); } void MCCefBrowserFactory::Finalize() { MCCefBrowserFinalise(); MCCefFinalise(); } bool MCCefBrowserFactory::CreateBrowser(void *p_display, void *p_parent_view, MCBrowser *&r_browser) { if (p_parent_view == nil) return false; MCBrowser *t_browser; t_browser = MCCefBrowserInstantiate(p_display, p_parent_view); if (t_browser == nil) return false; r_browser = t_browser; return true; } bool MCCefBrowserFactoryCreate(MCBrowserFactoryRef &r_factory) { MCCefBrowserFactory *t_factory; t_factory = new (nothrow) MCCefBrowserFactory(); if (t_factory == nil) return false; if (!t_factory->Initialize()) { delete t_factory; return false; } r_factory = t_factory; return true; }