// Copyright (c) 2012 CEF Python, see the Authors file.// All rights reserved. Licensed under BSD 3-clause license.// Project website: https://github.com/cztomczak/cefpython// BROWSER_PROCESS macro is defined when compiling the libcefpythonapp library.// RENDERER_PROCESS macro is define when compiling the subprocess executable.#ifdef BROWSER_PROCESS#include "common/cefpython_public_api.h"#endif#if defined(OS_WIN)#include <Shobjidl.h>#pragma comment(lib, "Shell32.lib")#endif // OS_WIN#ifdef BROWSER_PROCESS#ifdef OS_WIN#include "client_handler/dpi_aware.h"#elif OS_LINUX // OS_WIN#include <gtk/gtk.h>#include <gdk/gdk.h>#include <gdk/gdkx.h>#include "client_handler/x11.h"#include "print_handler_gtk.h"#endif // OS_LINUX#endif // BROWSER_PROCESS#include "cefpython_app.h"#include "util.h"#include "include/wrapper/cef_closure_task.h"#include "include/base/cef_bind.h"#include "include/base/cef_logging.h"#include <vector>#include <algorithm>#include "v8utils.h"#include "javascript_callback.h"#include "v8function_handler.h"#ifdef BROWSER_PROCESS#include "main_message_loop/main_message_loop_external_pump.h"#endif// GLOBALSbool g_debug = false;CefPythonApp::CefPythonApp() {#ifdef BROWSER_PROCESScefpython_GetDebugOptions(&g_debug);#endif}// -----------------------------------------------------------------------------// CefApp// -----------------------------------------------------------------------------void CefPythonApp::OnBeforeCommandLineProcessing(const CefString& process_type,CefRefPtr<CefCommandLine> command_line) {// IMPORTANT NOTES// ---------------// NOTE 1: Currently CEF logging is limited and you cannot log// info messages during execution of this function. The// default log severity in Chromium is LOG_ERROR, thus// you can will only see log messages when using LOG(ERROR)// here. You won't see LOG(WARNING) nor LOG(INFO) messages// here.// NOTE 2: The "g_debug" variable is never set at this moment// due to IPC messaging delay. There is some code here// that depends on this variable, but it is currently// never executed.#ifdef BROWSER_PROCESS// This is included only in the Browser process, when building// the libcefpythonapp library.if (process_type.empty()) {// Empty proc type, so this must be the main browser process.App_OnBeforeCommandLineProcessing_BrowserProcess(command_line);}#endif // BROWSER_PROCESS// IMPORTANT: This code is currently dead due to g_debug and// LOG(INFO) issues described at the top of this// function. Command line string for subprocesses are// are currently logged in OnBeforeChildProcess().// Command line string for the main browser process// is currently never logged.std::string process_name = process_type.ToString();if (process_name.empty()) {process_name = "browser";}#ifdef BROWSER_PROCESSstd::string logMessage = "[Browser process] ";#else // BROWSER_PROCESSstd::string logMessage = "[Non-browser process] ";#endif // BROWSER_PROCESSlogMessage.append("Command line string for the ");logMessage.append(process_name);logMessage.append(" process: ");std::string clString = command_line->GetCommandLineString().ToString();logMessage.append(clString.c_str());if (g_debug) {// This code is currently never executed, see the "IMPORTANT"// comment above and comments at the top of this function.LOG(INFO) << logMessage.c_str();}#ifdef OS_WIN// Set AppUserModelID (AppID) when "--app-user-model-id" switch// is provided. This fixes pinning to taskbar issues on Windows 10.// See Issue #395 for details.//// AppID here is set in all processes (Browser, Renderer, GPU, etc.).//// When compiling under Python 2.7 (VS2008) using Visual C++ for// Python, it seems you can't use WINSDK 7 header files even though// WINVER was specified in build_cpp_projects.py macros. Thus calling// using proc address. Specifying full path to Shell32.dll is not// required, in Chromium's source code full paths are not used.//// Note:// subprocess.exe is built with UNICODE defined, however the// cefpython_app library that is shared between subprocess and// python process does not define UNICODE macro. Thus it is required// to call LoadLibraryW explicitilly here.HINSTANCE shell32 = LoadLibraryW(L"shell32.dll");CefString app_id = command_line->GetSwitchValue("app-user-model-id");if (app_id.length()) {typedef HRESULT (WINAPI *SetAppUserModelID_Type)(PCWSTR);static SetAppUserModelID_Type SetAppUserModelID;SetAppUserModelID = (SetAppUserModelID_Type)GetProcAddress(shell32, "SetCurrentProcessExplicitAppUserModelID");HRESULT hr = (*SetAppUserModelID)(app_id.ToWString().c_str());if (hr == S_OK) {if (g_debug) {// This code is currently never executed, see comments// at the top of this function.LOG(INFO) << "SetCurrentProcessExplicitAppUserModelID ok";}} else {LOG(ERROR) << "SetCurrentProcessExplicitAppUserModelID failed";}}#endif // OS_WIN}void CefPythonApp::OnRegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar) {}CefRefPtr<CefResourceBundleHandler> CefPythonApp::GetResourceBundleHandler() {return NULL;}CefRefPtr<CefBrowserProcessHandler> CefPythonApp::GetBrowserProcessHandler() {return this;}CefRefPtr<CefRenderProcessHandler> CefPythonApp::GetRenderProcessHandler() {return this;}// ----------------------------------------------------------------------------// CefBrowserProcessHandler// ----------------------------------------------------------------------------void CefPythonApp::OnContextInitialized() {#ifdef BROWSER_PROCESSREQUIRE_UI_THREAD();#if defined(OS_LINUX)print_handler_ = new ClientPrintHandlerGtk();#endif // OS_LINUX#endif // BROWSER_PROCESS}void CefPythonApp::OnBeforeChildProcessLaunch(CefRefPtr<CefCommandLine> command_line) {#ifdef BROWSER_PROCESS// This is included only in the Browser process, when building// the libcefpythonapp library.#if defined(OS_WINDOWS)if (IsProcessDpiAware()) {// It is required to set DPI awareness in subprocesses// as well, see Issue #358.command_line->AppendSwitch("enable-high-dpi-support");}#endif // OS_WINDOWSBrowserProcessHandler_OnBeforeChildProcessLaunch(command_line);#endif // BROWSER_PROCESS#ifdef BROWSER_PROCESSstd::string logMessage = "[Browser process] ";#elsestd::string logMessage = "[Non-browser process] ";#endif // BROWSER_PROCESSlogMessage.append("OnBeforeChildProcessLaunch() command line: ");std::string clString = command_line->GetCommandLineString().ToString();logMessage.append(clString.c_str());LOG(INFO) << logMessage.c_str();}void CefPythonApp::OnRenderProcessThreadCreated(CefRefPtr<CefListValue> extra_info) {#ifdef BROWSER_PROCESS// If you have an existing CefListValue that you would like// to provide, do this:// | extra_info = mylist.get()// The equivalent in Cython is:// | extra_info.Assign(mylist.get())REQUIRE_IO_THREAD();// Eg.:// | extra_info->SetBool(0, false);// | extra_info->SetString(1, "test");// This is included only in the Browser process, when building// the libcefpythonapp library.BrowserProcessHandler_OnRenderProcessThreadCreated(extra_info);#endif // BROWSER_PROCESS}CefRefPtr<CefPrintHandler> CefPythonApp::GetPrintHandler() {#ifdef BROWSER_PROCESS#if defined(OS_LINUX)// For print handler to work GTK must be initialized. This is// required for some of the examples.// --// A similar code is in client_handler/x11.cpp. If making changes here,// make changes there as well.GdkDisplay* gdk_display = gdk_display_get_default();if (!gdk_display) {LOG(INFO) << "[Browser process] Initialize GTK";gtk_init(0, NULL);InstallX11ErrorHandlers();}#endif#endifreturn print_handler_;}void CefPythonApp::OnScheduleMessagePumpWork(int64 delay_ms) {#ifdef BROWSER_PROCESSMainMessageLoopExternalPump* message_pump =\MainMessageLoopExternalPump::Get();if (message_pump) {message_pump->OnScheduleMessagePumpWork(delay_ms);}#endif // BROWSER_PROCESS}// -----------------------------------------------------------------------------// CefRenderProcessHandler// -----------------------------------------------------------------------------void CefPythonApp::OnRenderThreadCreated(CefRefPtr<CefListValue> extra_info) {}void CefPythonApp::OnWebKitInitialized() {}void CefPythonApp::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {}void CefPythonApp::OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) {LOG(INFO) << "[Renderer process] OnBrowserDestroyed()";RemoveJavascriptBindings(browser);}void CefPythonApp::OnContextCreated(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefV8Context> context) {LOG(INFO) << "[Renderer process] OnContextCreated()";CefRefPtr<CefProcessMessage> message = CefProcessMessage::Create("OnContextCreated");CefRefPtr<CefListValue> arguments = message->GetArgumentList();/*Sending int64 type using process messaging would requireconverting it to a string or a binary, or you could sendtwo ints, see this topic:http://www.magpcss.org/ceforum/viewtopic.php?f=6&t=10869*//*// Example of converting int64 to string. Still need an// example of converting it back from string.std::string logMessage = "[Renderer process] OnContextCreated(): frameId=";stringstream stream;int64 value = frame->GetIdentifier();stream << value;logMessage.append(stream.str());LOG(INFO) << logMessage.c_str();*/// TODO: losing int64 precision, the solution is to convert// it to string and then in the Browser process back// from string to int64. But it is rather unlikely// that number of frames will exceed int range, so// casting it to int for now.arguments->SetInt(0, (int)(frame->GetIdentifier()));browser->SendProcessMessage(PID_BROWSER, message);CefRefPtr<CefDictionaryValue> jsBindings = GetJavascriptBindings(browser);if (jsBindings.get()) {// Javascript bindings are most probably not yet set for// the main frame, they will be set a moment later due to// process messaging delay. The code seems to be executed// only for iframes.if (frame->IsMain()) {DoJavascriptBindingsForFrame(browser, frame, context);} else {if (jsBindings->HasKey("bindToFrames")&& jsBindings->GetType("bindToFrames") == VTYPE_BOOL&& jsBindings->GetBool("bindToFrames")) {DoJavascriptBindingsForFrame(browser, frame, context);}}}}void CefPythonApp::OnContextReleased(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefV8Context> context) {LOG(INFO) << "[Renderer process] OnContextReleased()";CefRefPtr<CefProcessMessage> message;CefRefPtr<CefListValue> arguments;// ------------------------------------------------------------------------// 1. Send "OnContextReleased" message.// ------------------------------------------------------------------------message = CefProcessMessage::Create("OnContextReleased");arguments = message->GetArgumentList();arguments->SetInt(0, browser->GetIdentifier());// TODO: losing int64 precision, the solution is to convert// it to string and then in the Browser process back// from string to int64. But it is rather unlikely// that number of frames will exceed int range, so// casting it to int for now.arguments->SetInt(1, (int)(frame->GetIdentifier()));// Should we send the message using current "browser"// when this is not the main frame? It could fail, so// it is more reliable to always use the main browser.browser->SendProcessMessage(PID_BROWSER, message);// ------------------------------------------------------------------------// 2. Remove python callbacks for a frame.// ------------------------------------------------------------------------// This is already done via RemovePyFrame called from// V8ContextHandler_OnContextReleased.// If this is the main frame then in LifespanHandler_BeforeClose()// we're calling RemovePythonCallbacksForBrowser().// ------------------------------------------------------------------------// 3. Clear javascript callbacks.// ------------------------------------------------------------------------RemoveJavascriptCallbacksForFrame(frame);}void CefPythonApp::OnUncaughtException(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefV8Context> context,CefRefPtr<CefV8Exception> exception,CefRefPtr<CefV8StackTrace> stackTrace) {}void CefPythonApp::OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefDOMNode> node) {}bool CefPythonApp::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,CefProcessId source_process,CefRefPtr<CefProcessMessage> message) {std::string messageName = message->GetName().ToString();std::string logMessage = "[Renderer process] OnProcessMessageReceived(): ";logMessage.append(messageName.c_str());LOG(INFO) << logMessage.c_str();CefRefPtr<CefListValue> args = message->GetArgumentList();if (messageName == "DoJavascriptBindings") {if (args->GetSize() == 1&& args->GetType(0) == VTYPE_DICTIONARY&& args->GetDictionary(0)->IsValid()) {// Is it necessary to make a copy? It won't harm.SetJavascriptBindings(browser,args->GetDictionary(0)->Copy(false));DoJavascriptBindingsForBrowser(browser);} else {LOG(ERROR) << "[Renderer process] OnProcessMessageReceived():"" invalid arguments,"" messageName=DoJavascriptBindings";return false;}} else if (messageName == "ExecuteJavascriptCallback") {if (args->GetType(0) == VTYPE_INT) {int jsCallbackId = args->GetInt(0);CefRefPtr<CefListValue> jsArgs;if (args->IsReadOnly()) {jsArgs = args->Copy();} else {jsArgs = args;}// Remove jsCallbackId.jsArgs->Remove(0);ExecuteJavascriptCallback(jsCallbackId, jsArgs);} else {LOG(ERROR) << "[Renderer process] OnProcessMessageReceived:"" invalid arguments, expected first argument to be"" a javascript callback (int)";return false;}}return true;}void CefPythonApp::SetJavascriptBindings(CefRefPtr<CefBrowser> browser,CefRefPtr<CefDictionaryValue> data) {javascriptBindings_[browser->GetIdentifier()] = data;}CefRefPtr<CefDictionaryValue> CefPythonApp::GetJavascriptBindings(CefRefPtr<CefBrowser> browser) {int browserId = browser->GetIdentifier();if (javascriptBindings_.find(browserId) != javascriptBindings_.end()) {return javascriptBindings_[browserId];}return NULL;}void CefPythonApp::RemoveJavascriptBindings(CefRefPtr<CefBrowser> browser) {int browserId = browser->GetIdentifier();if (javascriptBindings_.find(browserId) != javascriptBindings_.end()) {javascriptBindings_.erase(browserId);}}bool CefPythonApp::BindedFunctionExists(CefRefPtr<CefBrowser> browser,const CefString& functionName) {CefRefPtr<CefDictionaryValue> jsBindings = GetJavascriptBindings(browser);if (!jsBindings.get()) {return false;}std::string strFunctionName = functionName.ToString();size_t dotPosition = strFunctionName.find(".");if (std::string::npos != dotPosition) {// This is a method call, functionName == "object.method".CefString objectName(strFunctionName.substr(0, dotPosition));CefString methodName(strFunctionName.substr(dotPosition + 1,std::string::npos));if (!(jsBindings->HasKey("objects")&& jsBindings->GetType("objects") == VTYPE_DICTIONARY)) {LOG(ERROR) << "[Renderer process] BindedFunctionExists():"" objects dictionary not found";return false;}CefRefPtr<CefDictionaryValue> objects = \jsBindings->GetDictionary("objects");if (objects->HasKey(objectName)) {if (!(objects->GetType(objectName) == VTYPE_DICTIONARY)) {LOG(ERROR) << "[Renderer process] BindedFunctionExists():"" objects dictionary has invalid type";return false;}CefRefPtr<CefDictionaryValue> methods = \objects->GetDictionary(objectName);return methods->HasKey(methodName);} else {return false;}} else {// This is a function call.if (!(jsBindings->HasKey("functions")&& jsBindings->GetType("functions") == VTYPE_DICTIONARY)) {LOG(ERROR) << "[Renderer process] BindedFunctionExists():"" functions dictionary not found";return false;}CefRefPtr<CefDictionaryValue> functions = \jsBindings->GetDictionary("functions");return functions->HasKey(functionName);}}void CefPythonApp::DoJavascriptBindingsForBrowser(CefRefPtr<CefBrowser> browser) {// get frame// get context// if bindToFrames is true loop through all frames,// otherwise just the main frame.// post task on a valid v8 threadCefRefPtr<CefDictionaryValue> jsBindings = GetJavascriptBindings(browser);if (!jsBindings.get()) {// Bindings must be set before this function is called.LOG(ERROR) << "[Renderer process] DoJavascriptBindingsForBrowser():"" bindings not set";return;}std::vector<int64> frameIds;std::vector<CefString> frameNames;if (jsBindings->HasKey("bindToFrames")&& jsBindings->GetType("bindToFrames") == VTYPE_BOOL&& jsBindings->GetBool("bindToFrames")) {// GetFrameIdentifiers() is buggy, returns always a vector// filled with zeroes (as of revision 1448). Use GetFrameNames()// instead.browser->GetFrameNames(frameNames);for (std::vector<CefString>::iterator it = frameNames.begin(); \it != frameNames.end(); ++it) {CefRefPtr<CefFrame> frame = browser->GetFrame(*it);if (frame.get()) {frameIds.push_back(frame->GetIdentifier());// | printf("GetFrameNames(): frameId = %lu\n",// | frame->GetIdentifier());}}}// BUG in CEF:// GetFrameNames() does not return the main frame (as of revision 1448).// Make it work for the future when this bug gets fixed.std::vector<int64>::iterator find_it = std::find(frameIds.begin(), frameIds.end(),browser->GetMainFrame()->GetIdentifier());if (find_it == frameIds.end()) {// Main frame not found in frameIds vector, add it now.// | printf("Adding main frame to frameIds: %lu\n",// | browser->GetMainFrame()->GetIdentifier());frameIds.push_back(browser->GetMainFrame()->GetIdentifier());}if (!frameIds.size()) {LOG(ERROR) << "[Renderer process] DoJavascriptBindingsForBrowser():"" frameIds.size() == 0";return;}for (std::vector<int64>::iterator it = frameIds.begin(); \it != frameIds.end(); ++it) {if (*it <= 0) {// GetFrameIdentifiers() bug that returned a vector// filled with zeros. This problem was fixed by using'// GetFrameNames() so this block of code should not// be executed anymore.LOG(ERROR) << "[Renderer process] DoJavascriptBindingsForBrowser():"" frameId <= 0";// printf("[CEF Python] Renderer: frameId = %lli\n", *it);continue;}CefRefPtr<CefFrame> frame = browser->GetFrame(*it);if (!frame.get()) {LOG(ERROR) << "[Renderer process] DoJavascriptBindingsForBrowser():"" GetFrame() failed";continue;}CefRefPtr<CefV8Context> context = frame->GetV8Context();CefRefPtr<CefTaskRunner> taskRunner = context->GetTaskRunner();taskRunner->PostTask(CefCreateClosureTask(base::Bind(&CefPythonApp::DoJavascriptBindingsForFrame, this,browser, frame, context)));}}void CefPythonApp::DoJavascriptBindingsForFrame(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefV8Context> context) {CefRefPtr<CefDictionaryValue> jsBindings = GetJavascriptBindings(browser);if (!jsBindings.get()) {// Bindings may not yet be set, it's okay.LOG(INFO) << "[Renderer process] DoJavascriptBindingsForFrame():"" bindings not set yet";return;}LOG(INFO) << "[Renderer process] DoJavascriptBindingsForFrame():"" bindings are set";if (!(jsBindings->HasKey("functions")&& jsBindings->GetType("functions") == VTYPE_DICTIONARY&& jsBindings->HasKey("properties")&& jsBindings->GetType("properties") == VTYPE_DICTIONARY&& jsBindings->HasKey("objects")&& jsBindings->GetType("objects") == VTYPE_DICTIONARY&& jsBindings->HasKey("bindToFrames")&& jsBindings->GetType("bindToFrames") == VTYPE_BOOL)) {LOG(ERROR) << "[Renderer process] DoJavascriptBindingsForFrame():"" invalid data [1]";return;}// A context must be explicitly entered before creating a// V8 Object, Array, Function or Date asynchronously.// NOTE: you cannot call CefV8Context::GetEnteredContext// or GetCurrentContext when CefV8Context::InContext// returns false, as it will result in crashes.bool didEnterContext = false;if (!CefV8Context::InContext()) {if (!context->IsValid()) {// BUG in CEF (Issue 130), the "context" provided by CEF may// not be valid. May be a timing issue. Or may be caused by// a redirect to a different origin and that creates a new// renderer process.// This message is logged in the tutorial.py example which// uses data uri created from html string.LOG(INFO) << "[Renderer process] DoJavascriptBindingsForFrame():"" V8 context provided by CEF is invalid";return;}context->Enter();didEnterContext = true;}CefRefPtr<CefDictionaryValue> functions = \jsBindings->GetDictionary("functions");CefRefPtr<CefDictionaryValue> properties = \jsBindings->GetDictionary("properties");CefRefPtr<CefDictionaryValue> objects = \jsBindings->GetDictionary("objects");// Here in this function we bind only for the current frame.// | bool bindToFrames = jsBindings->GetBool("bindToFrames");if (!(functions->IsValid() && properties->IsValid()&& objects->IsValid())) {LOG(ERROR) << "[Renderer process] DoJavascriptBindingsForFrame():"" invalid data [2]";if (didEnterContext)context->Exit();return;}CefRefPtr<CefV8Value> v8Window = context->GetGlobal();CefRefPtr<CefV8Value> v8Function;CefRefPtr<CefV8Handler> v8FunctionHandler(new V8FunctionHandler(this, 0));// FUNCTIONS.std::vector<CefString> functionsVector;if (!functions->GetKeys(functionsVector)) {LOG(ERROR) << "[Renderer process] DoJavascriptBindingsForFrame():"" functions->GetKeys() failed";if (didEnterContext)context->Exit();return;}for (std::vector<CefString>::iterator it = functionsVector.begin(); \it != functionsVector.end(); ++it) {CefString functionName = *it;v8Function = CefV8Value::CreateFunction(functionName,v8FunctionHandler);v8Window->SetValue(functionName, v8Function,V8_PROPERTY_ATTRIBUTE_NONE);}// PROPERTIES.CefRefPtr<CefV8Value> v8Properties = CefDictionaryValueToV8Value(properties);std::vector<CefString> v8Keys;if (!v8Properties->GetKeys(v8Keys)) {LOG(ERROR) << "[Renderer process] DoJavascriptBindingsForFrame():"" v8Properties->GetKeys() failed";if (didEnterContext)context->Exit();return;}for (std::vector<CefString>::iterator it = v8Keys.begin(); \it != v8Keys.end(); ++it) {CefString v8Key = *it;CefRefPtr<CefV8Value> v8Value = v8Properties->GetValue(v8Key);v8Window->SetValue(v8Key, v8Value, V8_PROPERTY_ATTRIBUTE_NONE);}// OBJECTS AND ITS METHODS.std::vector<CefString> objectsVector;if (!objects->GetKeys(objectsVector)) {LOG(ERROR) << "[Renderer process] DoJavascriptBindingsForFrame():"" objects->GetKeys() failed";if (didEnterContext)context->Exit();return;}for (std::vector<CefString>::iterator it = objectsVector.begin(); \it != objectsVector.end(); ++it) {CefString objectName = *it;CefRefPtr<CefV8Value> v8Object = CefV8Value::CreateObject(NULL, NULL);v8Window->SetValue(objectName, v8Object, V8_PROPERTY_ATTRIBUTE_NONE);// METHODS.if (!(objects->GetType(objectName) == VTYPE_DICTIONARY)) {LOG(ERROR) << "[Renderer process] DoJavascriptBindingsForFrame():"" objects->GetType() != VTYPE_DICTIONARY";if (didEnterContext)context->Exit();return;}CefRefPtr<CefDictionaryValue> methods = \objects->GetDictionary(objectName);std::vector<CefString> methodsVector;if (!(methods->IsValid() && methods->GetKeys(methodsVector))) {LOG(ERROR) << "[Renderer process] DoJavascriptBindingsForFrame():"" methods->GetKeys() failed";if (didEnterContext)context->Exit();return;}for (std::vector<CefString>::iterator it = methodsVector.begin(); \it != methodsVector.end(); ++it) {CefString methodName = *it;// fullMethodName = "object.method"std::string fullMethodName = objectName.ToString().append(".") \.append(methodName.ToString());v8Function = CefV8Value::CreateFunction(fullMethodName,v8FunctionHandler);v8Object->SetValue(methodName, v8Function,V8_PROPERTY_ATTRIBUTE_NONE);}}// END.if (didEnterContext)context->Exit();}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。