cef/tests/cefclient/cefclient_win.cpp

798 lines
24 KiB
C++

// Copyright (c) 2010 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 "include/cef.h"
#include "include/cef_runnable.h"
#include "cefclient.h"
#include "binding_test.h"
#include "client_handler.h"
#include "extension_test.h"
#include "osrplugin_test.h"
#include "plugin_test.h"
#include "resource.h"
#include "scheme_test.h"
#include "string_util.h"
#include "uiplugin_test.h"
#include <commdlg.h>
#include <direct.h>
#include <sstream>
#include <shellapi.h>
#define MAX_LOADSTRING 100
#define MAX_URL_LENGTH 255
#define BUTTON_WIDTH 72
#define URLBAR_HEIGHT 24
// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
char szWorkingDir[MAX_PATH]; // The current working directory
UINT uFindMsg; // Message identifier for find events.
HWND hFindDlg = NULL; // Handle for the find dialog.
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
// The global ClientHandler reference.
extern CefRefPtr<ClientHandler> g_handler;
#if defined(OS_WIN)
// Add Common Controls to the application manifest because it's required to
// support the default tooltip implementation.
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
// Program entry point function.
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Retrieve the current working directory.
if(_getcwd(szWorkingDir, MAX_PATH) == NULL)
szWorkingDir[0] = 0;
// Parse command line arguments. The passed in values are ignored on Windows.
AppInitCommandLine(0, NULL);
CefSettings settings;
// Populate the settings based on command line arguments.
AppGetSettings(settings);
// Initialize CEF.
CefInitialize(settings);
// Register the internal client plugin.
InitPluginTest();
// Register the internal UI client plugin.
InitUIPluginTest();
// Register the internal OSR client plugin.
InitOSRPluginTest();
// Register the V8 extension handler.
InitExtensionTest();
// Register the scheme handler.
InitSchemeTest();
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_CEFCLIENT, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CEFCLIENT));
// Register the find event message.
uFindMsg = RegisterWindowMessage(FINDMSGSTRING);
int result = 0;
if (!settings.multi_threaded_message_loop) {
// Run the CEF message loop. This function will block until the application
// recieves a WM_QUIT message.
CefRunMessageLoop();
} else {
MSG msg;
// Run the application message loop.
while (GetMessage(&msg, NULL, 0, 0)) {
// Allow processing of find dialog messages.
if (hFindDlg && IsDialogMessage(hFindDlg, &msg))
continue;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
result = (int)msg.wParam;
}
// Shut down CEF.
CefShutdown();
return result;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
// This function and its usage are only necessary if you want this code
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this
// function so that the application will get 'well formed' small icons
// associated with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CEFCLIENT));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_CEFCLIENT);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, 0, CW_USEDEFAULT,
0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND backWnd = NULL, forwardWnd = NULL, reloadWnd = NULL,
stopWnd = NULL, editWnd = NULL;
static WNDPROC editWndOldProc = NULL;
// Static members used for the find dialog.
static FINDREPLACE fr;
static WCHAR szFindWhat[80] = {0};
static WCHAR szLastFindWhat[80] = {0};
static bool findNext = false;
static bool lastMatchCase = false;
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
if(hWnd == editWnd)
{
// Callback for the edit window
switch (message)
{
case WM_CHAR:
if (wParam == VK_RETURN && g_handler.get())
{
// When the user hits the enter key load the URL
CefRefPtr<CefBrowser> browser = g_handler->GetBrowser();
wchar_t strPtr[MAX_URL_LENGTH] = {0};
*((LPWORD)strPtr) = MAX_URL_LENGTH;
LRESULT strLen = SendMessage(hWnd, EM_GETLINE, 0, (LPARAM)strPtr);
if (strLen > 0) {
strPtr[strLen] = 0;
browser->GetMainFrame()->LoadURL(strPtr);
}
return 0;
}
}
return (LRESULT)CallWindowProc(editWndOldProc, hWnd, message, wParam, lParam);
}
else if (message == uFindMsg)
{
// Find event.
LPFINDREPLACE lpfr = (LPFINDREPLACE)lParam;
if (lpfr->Flags & FR_DIALOGTERM)
{
// The find dialog box has been dismissed so invalidate the handle and
// reset the search results.
hFindDlg = NULL;
if(g_handler.get())
{
g_handler->GetBrowser()->StopFinding(true);
szLastFindWhat[0] = 0;
findNext = false;
}
return 0;
}
if ((lpfr->Flags & FR_FINDNEXT) && g_handler.get())
{
// Search for the requested string.
bool matchCase = (lpfr->Flags & FR_MATCHCASE?true:false);
if(matchCase != lastMatchCase ||
(matchCase && wcsncmp(szFindWhat, szLastFindWhat,
sizeof(szLastFindWhat)/sizeof(WCHAR)) != 0) ||
(!matchCase && _wcsnicmp(szFindWhat, szLastFindWhat,
sizeof(szLastFindWhat)/sizeof(WCHAR)) != 0))
{
// The search string has changed, so reset the search results.
if(szLastFindWhat[0] != 0) {
g_handler->GetBrowser()->StopFinding(true);
findNext = false;
}
lastMatchCase = matchCase;
wcscpy_s(szLastFindWhat, sizeof(szLastFindWhat)/sizeof(WCHAR),
szFindWhat);
}
g_handler->GetBrowser()->Find(0, lpfr->lpstrFindWhat,
(lpfr->Flags & FR_DOWN)?true:false, matchCase, findNext);
if(!findNext)
findNext = true;
}
return 0;
}
else
{
// Callback for the main window
switch (message)
{
case WM_CREATE:
{
// Create the single static handler class instance
g_handler = new ClientHandler();
g_handler->SetMainHwnd(hWnd);
// Create the child windows used for navigation
RECT rect;
int x = 0;
GetClientRect(hWnd, &rect);
backWnd = CreateWindow(L"BUTTON", L"Back",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON
| WS_DISABLED, x, 0, BUTTON_WIDTH, URLBAR_HEIGHT,
hWnd, (HMENU) IDC_NAV_BACK, hInst, 0);
x += BUTTON_WIDTH;
forwardWnd = CreateWindow(L"BUTTON", L"Forward",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON
| WS_DISABLED, x, 0, BUTTON_WIDTH,
URLBAR_HEIGHT, hWnd, (HMENU) IDC_NAV_FORWARD,
hInst, 0);
x += BUTTON_WIDTH;
reloadWnd = CreateWindow(L"BUTTON", L"Reload",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON
| WS_DISABLED, x, 0, BUTTON_WIDTH,
URLBAR_HEIGHT, hWnd, (HMENU) IDC_NAV_RELOAD,
hInst, 0);
x += BUTTON_WIDTH;
stopWnd = CreateWindow(L"BUTTON", L"Stop",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON
| WS_DISABLED, x, 0, BUTTON_WIDTH, URLBAR_HEIGHT,
hWnd, (HMENU) IDC_NAV_STOP, hInst, 0);
x += BUTTON_WIDTH;
editWnd = CreateWindow(L"EDIT", 0,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT |
ES_AUTOVSCROLL | ES_AUTOHSCROLL| WS_DISABLED,
x, 0, rect.right - BUTTON_WIDTH * 4,
URLBAR_HEIGHT, hWnd, 0, hInst, 0);
// Assign the edit window's WNDPROC to this function so that we can
// capture the enter key
editWndOldProc =
reinterpret_cast<WNDPROC>(GetWindowLongPtr(editWnd, GWLP_WNDPROC));
SetWindowLongPtr(editWnd, GWLP_WNDPROC,
reinterpret_cast<LONG_PTR>(WndProc));
g_handler->SetEditHwnd(editWnd);
g_handler->SetButtonHwnds(backWnd, forwardWnd, reloadWnd, stopWnd);
rect.top += URLBAR_HEIGHT;
CefWindowInfo info;
CefBrowserSettings settings;
// Populate the settings based on command line arguments.
AppGetBrowserSettings(settings);
// Initialize window info to the defaults for a child window
info.SetAsChild(hWnd, rect);
// Creat the new child browser window
CefBrowser::CreateBrowser(info,
static_cast<CefRefPtr<CefClient> >(g_handler),
"http://www.google.com", settings);
}
return 0;
case WM_COMMAND:
{
CefRefPtr<CefBrowser> browser;
if(g_handler.get())
browser = g_handler->GetBrowser();
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
return 0;
case IDM_EXIT:
DestroyWindow(hWnd);
return 0;
case ID_WARN_CONSOLEMESSAGE:
if(g_handler.get()) {
std::wstringstream ss;
ss << L"Console messages will be written to "
<< std::wstring(CefString(g_handler->GetLogFile()));
MessageBox(hWnd, ss.str().c_str(), L"Console Messages",
MB_OK | MB_ICONINFORMATION);
}
return 0;
case ID_WARN_DOWNLOADCOMPLETE:
case ID_WARN_DOWNLOADERROR:
if(g_handler.get()) {
std::wstringstream ss;
ss << L"File \"" <<
std::wstring(CefString(g_handler->GetLastDownloadFile())) <<
L"\" ";
if(wmId == ID_WARN_DOWNLOADCOMPLETE)
ss << L"downloaded successfully.";
else
ss << L"failed to download.";
MessageBox(hWnd, ss.str().c_str(), L"File Download",
MB_OK | MB_ICONINFORMATION);
}
return 0;
case ID_FIND:
if(!hFindDlg)
{
// Create the find dialog.
ZeroMemory(&fr, sizeof(fr));
fr.lStructSize = sizeof(fr);
fr.hwndOwner = hWnd;
fr.lpstrFindWhat = szFindWhat;
fr.wFindWhatLen = sizeof(szFindWhat);
fr.Flags = FR_HIDEWHOLEWORD | FR_DOWN;
hFindDlg = FindText(&fr);
}
else
{
// Give focus to the existing find dialog.
::SetFocus(hFindDlg);
}
return 0;
case ID_PRINT:
if(browser.get())
browser->GetMainFrame()->Print();
return 0;
case IDC_NAV_BACK: // Back button
if(browser.get())
browser->GoBack();
return 0;
case IDC_NAV_FORWARD: // Forward button
if(browser.get())
browser->GoForward();
return 0;
case IDC_NAV_RELOAD: // Reload button
if(browser.get())
browser->Reload();
return 0;
case IDC_NAV_STOP: // Stop button
if(browser.get())
browser->StopLoad();
return 0;
case ID_TESTS_GETSOURCE: // Test the GetSource function
if(browser.get())
RunGetSourceTest(browser);
return 0;
case ID_TESTS_GETTEXT: // Test the GetText function
if(browser.get())
RunGetTextTest(browser);
return 0;
case ID_TESTS_JAVASCRIPT_BINDING: // Test the V8 binding handler
if(browser.get())
RunBindingTest(browser);
return 0;
case ID_TESTS_JAVASCRIPT_EXTENSION: // Test the V8 extension handler
if(browser.get())
RunExtensionTest(browser);
return 0;
case ID_TESTS_JAVASCRIPT_PERFORMANCE: // Test the V8 performance
if(browser.get())
RunExtensionPerfTest(browser);
return 0;
case ID_TESTS_JAVASCRIPT_EXECUTE: // Test execution of javascript
if(browser.get())
RunJavaScriptExecuteTest(browser);
return 0;
case ID_TESTS_JAVASCRIPT_INVOKE:
if(browser.get())
RunJavaScriptInvokeTest(browser);
return 0;
case ID_TESTS_PLUGIN: // Test the custom plugin
if(browser.get())
RunPluginTest(browser);
return 0;
case ID_TESTS_POPUP: // Test a popup window
if(browser.get())
RunPopupTest(browser);
return 0;
case ID_TESTS_TRANSPARENT_POPUP: // Test a transparent popup window
if(browser.get())
RunTransparentPopupTest(browser);
return 0;
case ID_TESTS_REQUEST: // Test a request
if(browser.get())
RunRequestTest(browser);
return 0;
case ID_TESTS_SCHEME_HANDLER: // Test the scheme handler
if(browser.get())
RunSchemeTest(browser);
return 0;
case ID_TESTS_UIAPP: // Test the UI app
if(browser.get())
RunUIPluginTest(browser);
return 0;
case ID_TESTS_OSRAPP: // Test the OSR app
if(browser.get())
RunOSRPluginTest(browser, false);
return 0;
case ID_TESTS_TRANSPARENT_OSRAPP: // Test the OSR app with transparency
if(browser.get())
RunOSRPluginTest(browser, true);
return 0;
case ID_TESTS_DOMACCESS: // Test DOM access
if(browser.get())
RunDOMAccessTest(browser);
return 0;
case ID_TESTS_LOCALSTORAGE: // Test localStorage
if(browser.get())
RunLocalStorageTest(browser);
return 0;
case ID_TESTS_ACCELERATED2DCANVAS: // Test accelerated 2d canvas
if(browser.get())
RunAccelerated2DCanvasTest(browser);
return 0;
case ID_TESTS_ACCELERATEDLAYERS: // Test accelerated layers
if(browser.get())
RunAcceleratedLayersTest(browser);
return 0;
case ID_TESTS_WEBGL: // Test WebGL
if(browser.get())
RunWebGLTest(browser);
return 0;
case ID_TESTS_HTML5VIDEO: // Test HTML5 video
if(browser.get())
RunHTML5VideoTest(browser);
return 0;
case ID_TESTS_DRAGDROP: // Test drag & drop
if(browser.get())
RunDragDropTest(browser);
return 0;
case ID_TESTS_XMLHTTPREQUEST: // Test XMLHttpRequest
if(browser.get())
RunXMLHTTPRequestTest(browser);
return 0;
case ID_TESTS_WEBURLREQUEST:
if (browser.get())
RunWebURLRequestTest(browser);
return 0;
case ID_TESTS_ZOOM_IN:
if(browser.get())
browser->SetZoomLevel(browser->GetZoomLevel() + 0.5);
return 0;
case ID_TESTS_ZOOM_OUT:
if(browser.get())
browser->SetZoomLevel(browser->GetZoomLevel() - 0.5);
return 0;
case ID_TESTS_ZOOM_RESET:
if(browser.get())
browser->SetZoomLevel(0.0);
return 0;
case ID_TESTS_DEVTOOLS_SHOW:
if (browser.get())
browser->ShowDevTools();
return 0;
case ID_TESTS_DEVTOOLS_CLOSE:
if (browser.get())
browser->CloseDevTools();
return 0;
case ID_TESTS_MODALDIALOG:
if(browser.get())
RunModalDialogTest(browser);
return 0;
case ID_TESTS_GETIMAGE:
if(browser.get())
RunGetImageTest(browser);
return 0;
}
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
case WM_SETFOCUS:
if(g_handler.get() && g_handler->GetBrowserHwnd())
{
// Pass focus to the browser window
PostMessage(g_handler->GetBrowserHwnd(), WM_SETFOCUS, wParam, NULL);
}
return 0;
case WM_SIZE:
if(g_handler.get() && g_handler->GetBrowserHwnd())
{
// Resize the browser window and address bar to match the new frame
// window size
RECT rect;
GetClientRect(hWnd, &rect);
rect.top += URLBAR_HEIGHT;
int urloffset = rect.left + BUTTON_WIDTH * 4;
HDWP hdwp = BeginDeferWindowPos(1);
hdwp = DeferWindowPos(hdwp, editWnd, NULL, urloffset,
0, rect.right - urloffset, URLBAR_HEIGHT, SWP_NOZORDER);
hdwp = DeferWindowPos(hdwp, g_handler->GetBrowserHwnd(), NULL,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
SWP_NOZORDER);
EndDeferWindowPos(hdwp);
}
break;
case WM_ERASEBKGND:
if(g_handler.get() && g_handler->GetBrowserHwnd())
{
// Dont erase the background if the browser window has been loaded
// (this avoids flashing)
return 0;
}
break;
case WM_CLOSE:
if (g_handler.get()) {
CefRefPtr<CefBrowser> browser = g_handler->GetBrowser();
if (browser.get()) {
// Let the browser window know we are about to destroy it.
browser->ParentWindowWillClose();
}
}
break;
case WM_DESTROY:
// The frame window has exited
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
// Global functions
std::string AppGetWorkingDirectory()
{
return szWorkingDir;
}
void RunTransparentPopupTest(CefRefPtr<CefBrowser> browser)
{
CefWindowInfo info;
CefBrowserSettings settings;
// Initialize window info to the defaults for a popup window
info.SetAsPopup(NULL, "TransparentPopup");
info.SetTransparentPainting(TRUE);
info.m_nWidth = 500;
info.m_nHeight = 500;
// Creat the popup browser window
CefBrowser::CreateBrowser(info,
static_cast<CefRefPtr<CefClient> >(g_handler),
"http://tests/transparency", settings);
}
namespace {
// Determine a temporary path for the bitmap file.
bool GetBitmapTempPath(LPWSTR szTempName)
{
DWORD dwRetVal;
DWORD dwBufSize = 512;
TCHAR lpPathBuffer[512];
UINT uRetVal;
dwRetVal = GetTempPath(dwBufSize, // length of the buffer
lpPathBuffer); // buffer for path
if (dwRetVal > dwBufSize || (dwRetVal == 0))
return false;
// Create a temporary file.
uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files
L"image", // temp file name prefix
0, // create unique name
szTempName); // buffer for name
if (uRetVal == 0)
return false;
size_t len = wcslen(szTempName);
wcscpy(szTempName + len - 3, L"bmp");
return true;
}
void UIT_RunGetImageTest(CefRefPtr<CefBrowser> browser)
{
REQUIRE_UI_THREAD();
int width, height;
bool success = false;
// Retrieve the image size.
if (browser->GetSize(PET_VIEW, width, height)) {
void* bits;
// Populate the bitmap info header.
BITMAPINFOHEADER info;
info.biSize = sizeof(BITMAPINFOHEADER);
info.biWidth = width;
info.biHeight = -height; // minus means top-down bitmap
info.biPlanes = 1;
info.biBitCount = 32;
info.biCompression = BI_RGB; // no compression
info.biSizeImage = 0;
info.biXPelsPerMeter = 1;
info.biYPelsPerMeter = 1;
info.biClrUsed = 0;
info.biClrImportant = 0;
// Create the bitmap and retrieve the bit buffer.
HDC screen_dc = GetDC(NULL);
HBITMAP bitmap =
CreateDIBSection(screen_dc, reinterpret_cast<BITMAPINFO*>(&info),
DIB_RGB_COLORS, &bits, NULL, 0);
ReleaseDC(NULL, screen_dc);
// Read the image into the bit buffer.
if (bitmap && browser->GetImage(PET_VIEW, width, height, bits)) {
// Populate the bitmap file header.
BITMAPFILEHEADER file;
file.bfType = 0x4d42;
file.bfSize = sizeof(BITMAPFILEHEADER);
file.bfReserved1 = 0;
file.bfReserved2 = 0;
file.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
TCHAR temp_path[512];
if (GetBitmapTempPath(temp_path)) {
// Write the bitmap to file.
HANDLE file_handle =
CreateFile(temp_path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, 0);
if (file_handle != INVALID_HANDLE_VALUE) {
DWORD bytes_written = 0;
WriteFile(file_handle, &file, sizeof(file), &bytes_written, 0);
WriteFile(file_handle, &info, sizeof(info), &bytes_written, 0);
WriteFile(file_handle, bits, width * height * 4, &bytes_written, 0);
CloseHandle(file_handle);
// Open the bitmap in the default viewer.
ShellExecute(NULL, L"open", temp_path, NULL, NULL, SW_SHOWNORMAL);
success = true;
}
}
}
DeleteObject(bitmap);
}
if (!success) {
browser->GetMainFrame()->ExecuteJavaScript(
"alert('Failed to create image!');",
browser->GetMainFrame()->GetURL(), 0);
}
}
} // namespace
void RunGetImageTest(CefRefPtr<CefBrowser> browser)
{
// Execute the test function on the UI thread.
CefPostTask(TID_UI, NewCefRunnableFunction(UIT_RunGetImageTest, browser));
}