From d7c54d65146e8e29aaa851a691ce513342ac918c Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Fri, 21 Aug 2009 19:43:37 +0000 Subject: [PATCH] cefclient: - Add the User Interface App Example that demonstrates two-way JavaScript communication between the page and an embedded plugin implemented using OpenGL. git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@40 5089003a-bbd8-11dd-ad1f-f1f9622dbc98 --- tests/cefclient/cefclient.cpp | 81 +++++++-- tests/cefclient/cefclient.h | 7 + tests/cefclient/cefclient.rc | 3 + tests/cefclient/cefclient.vcproj | 16 ++ tests/cefclient/cefclient.vsprops | 2 +- tests/cefclient/clientplugin.cpp | 4 +- tests/cefclient/res/logoball.png | Bin 0 -> 7181 bytes tests/cefclient/res/uiplugin.html | 57 ++++++ tests/cefclient/resource.h | 3 + tests/cefclient/uiplugin.cpp | 284 ++++++++++++++++++++++++++++++ tests/cefclient/uiplugin.h | 22 +++ tests/cefclient/uiplugin_test.cpp | 109 ++++++++++++ tests/cefclient/uiplugin_test.h | 18 ++ 13 files changed, 585 insertions(+), 21 deletions(-) create mode 100644 tests/cefclient/res/logoball.png create mode 100644 tests/cefclient/res/uiplugin.html create mode 100644 tests/cefclient/uiplugin.cpp create mode 100644 tests/cefclient/uiplugin.h create mode 100644 tests/cefclient/uiplugin_test.cpp create mode 100644 tests/cefclient/uiplugin_test.h diff --git a/tests/cefclient/cefclient.cpp b/tests/cefclient/cefclient.cpp index 72d33566f..b9328d3ac 100644 --- a/tests/cefclient/cefclient.cpp +++ b/tests/cefclient/cefclient.cpp @@ -11,6 +11,7 @@ #include "resource_util.h" #include "scheme_test.h" #include "string_util.h" +#include "uiplugin_test.h" #include @@ -56,6 +57,9 @@ int APIENTRY _tWinMain(HINSTANCE hInstance, // Register the internal client plugin. InitPluginTest(); + // Register the internal UI client plugin. + InitUIPluginTest(); + // Register the V8 extension handler. InitExtensionTest(); @@ -341,6 +345,9 @@ public: std::wstring& mimeType, int loadFlags) { + DWORD dwSize; + LPBYTE pBytes; + std::wstring url = request->GetURL(); if(url == L"http://tests/request") { // Show the request contents @@ -349,15 +356,27 @@ public: resourceStream = CefStreamReader::CreateForData( (void*)dump.c_str(), dump.size() * sizeof(wchar_t)); mimeType = L"text/plain"; - } else if(wcsstr(url.c_str(), L"logo.gif") != NULL) { + } else if(url == L"http://tests/uiapp") { + // Show the uiapp contents + if(LoadBinaryResource(IDS_UIPLUGIN, dwSize, pBytes)) { + resourceStream = CefStreamReader::CreateForHandler( + new ClientReadHandler(pBytes, dwSize)); + mimeType = L"text/html"; + } + } else if(wcsstr(url.c_str(), L"/logo.gif") != NULL) { // Any time we find "logo.gif" in the URL substitute in our own image - DWORD dwSize; - LPBYTE pBytes; if(LoadBinaryResource(IDS_LOGO, dwSize, pBytes)) { resourceStream = CefStreamReader::CreateForHandler( new ClientReadHandler(pBytes, dwSize)); mimeType = L"image/jpg"; } + } else if(wcsstr(url.c_str(), L"/logoball.png") != NULL) { + // Load the "logoball.png" image resource. + if(LoadBinaryResource(IDS_LOGOBALL, dwSize, pBytes)) { + resourceStream = CefStreamReader::CreateForHandler( + new ClientReadHandler(pBytes, dwSize)); + mimeType = L"image/png"; + } } return RV_CONTINUE; } @@ -485,6 +504,9 @@ public: { // Add the V8 bindings. InitBindingTest(browser, frame, object); + + // Add the UI app V8 bindings. + InitUIBindingTest(browser, frame, object); return RV_HANDLED; } @@ -515,6 +537,7 @@ public: m_MainHwnd = hwnd; Unlock(); } + HWND GetMainHwnd() { return m_MainHwnd; } void SetEditHwnd(HWND hwnd) { @@ -554,6 +577,23 @@ protected: bool m_bCanGoForward; }; +// global handler instance +CefRefPtr g_handler; + +CefRefPtr AppGetBrowser() +{ + if(!g_handler.get()) + return NULL; + return g_handler->GetBrowser(); +} + +HWND AppGetMainHwnd() +{ + if(!g_handler.get()) + return NULL; + return g_handler->GetMainHwnd(); +} + // // FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) @@ -562,7 +602,6 @@ protected: // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - static CefRefPtr handler; static HWND backWnd = NULL, forwardWnd = NULL, reloadWnd = NULL, stopWnd = NULL, editWnd = NULL; static WNDPROC editWndOldProc = NULL; @@ -577,10 +616,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) switch (message) { case WM_CHAR: - if (wParam == VK_RETURN && handler.get()) + if (wParam == VK_RETURN && g_handler.get()) { // When the user hits the enter key load the URL - CefRefPtr browser = handler->GetBrowser(); + CefRefPtr 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); @@ -603,8 +642,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_CREATE: { // Create the single static handler class instance - handler = new ClientHandler(); - handler->SetMainHwnd(hWnd); + g_handler = new ClientHandler(); + g_handler->SetMainHwnd(hWnd); // Create the child windows used for navigation RECT rect; @@ -650,7 +689,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) reinterpret_cast(GetWindowLongPtr(editWnd, GWLP_WNDPROC)); SetWindowLongPtr(editWnd, GWLP_WNDPROC, reinterpret_cast(WndProc)); - handler->SetEditHwnd(editWnd); + g_handler->SetEditHwnd(editWnd); rect.top += URLBAR_HEIGHT; @@ -661,7 +700,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Creat the new child child browser window CefBrowser::CreateBrowser(info, false, - static_cast>(handler), + static_cast>(g_handler), L"http://www.google.com"); // Start the timer that will be used to update child window state @@ -670,11 +709,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) return 0; case WM_TIMER: - if(handler.get() && handler->GetBrowserHwnd()) + if(g_handler.get() && g_handler->GetBrowserHwnd()) { // Retrieve the current navigation state bool isLoading, canGoBack, canGoForward; - handler->GetNavState(isLoading, canGoBack, canGoForward); + g_handler->GetNavState(isLoading, canGoBack, canGoForward); // Update the status of child windows EnableWindow(editWnd, TRUE); @@ -688,8 +727,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_COMMAND: { CefRefPtr browser; - if(handler.get()) - browser = handler->GetBrowser(); + if(g_handler.get()) + browser = g_handler->GetBrowser(); wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); @@ -779,6 +818,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) if(browser.get()) RunSchemeTest(browser); return 0; + case ID_TESTS_UIAPP: // Test the UI app + if(browser.get()) + RunUIPluginTest(browser); + return 0; } } break; @@ -789,15 +832,15 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) return 0; case WM_SETFOCUS: - if(handler.get() && handler->GetBrowserHwnd()) + if(g_handler.get() && g_handler->GetBrowserHwnd()) { // Pass focus to the browser window - PostMessage(handler->GetBrowserHwnd(), WM_SETFOCUS, wParam, NULL); + PostMessage(g_handler->GetBrowserHwnd(), WM_SETFOCUS, wParam, NULL); } return 0; case WM_SIZE: - if(handler.get() && handler->GetBrowserHwnd()) + if(g_handler.get() && g_handler->GetBrowserHwnd()) { // Resize the browser window and address bar to match the new frame // window size @@ -810,7 +853,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) HDWP hdwp = BeginDeferWindowPos(1); hdwp = DeferWindowPos(hdwp, editWnd, NULL, urloffset, 0, rect.right - urloffset, URLBAR_HEIGHT, SWP_NOZORDER); - hdwp = DeferWindowPos(hdwp, handler->GetBrowserHwnd(), NULL, + hdwp = DeferWindowPos(hdwp, g_handler->GetBrowserHwnd(), NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER); EndDeferWindowPos(hdwp); @@ -818,7 +861,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; case WM_ERASEBKGND: - if(handler.get() && handler->GetBrowserHwnd()) + if(g_handler.get() && g_handler->GetBrowserHwnd()) { // Dont erase the background if the browser window has been loaded // (this avoids flashing) diff --git a/tests/cefclient/cefclient.h b/tests/cefclient/cefclient.h index 2b538cb52..1895bc200 100644 --- a/tests/cefclient/cefclient.h +++ b/tests/cefclient/cefclient.h @@ -5,3 +5,10 @@ #pragma once #include "resource.h" +#include "cef.h" + +// Return the main browser window instance. +CefRefPtr AppGetBrowser(); + +// Return the main application window handle. +HWND AppGetMainHwnd(); diff --git a/tests/cefclient/cefclient.rc b/tests/cefclient/cefclient.rc index 3655dfb2e..42ef620d2 100644 --- a/tests/cefclient/cefclient.rc +++ b/tests/cefclient/cefclient.rc @@ -29,6 +29,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // IDS_LOGO BINARY "res\logo.jpg" +IDS_UIPLUGIN BINARY "res\uiplugin.html" +IDS_LOGOBALL BINARY "res\logoball.png" ///////////////////////////////////////////////////////////////////////////// // @@ -64,6 +66,7 @@ BEGIN MENUITEM "Popup Window", ID_TESTS_POPUP MENUITEM "Request", ID_TESTS_REQUEST MENUITEM "Scheme Handler", ID_TESTS_SCHEME_HANDLER + MENUITEM "UI App Example", ID_TESTS_UIAPP END END diff --git a/tests/cefclient/cefclient.vcproj b/tests/cefclient/cefclient.vcproj index ed6fde850..e54700fb9 100644 --- a/tests/cefclient/cefclient.vcproj +++ b/tests/cefclient/cefclient.vcproj @@ -265,6 +265,22 @@ RelativePath=".\string_util.h" > + + + + + + + + diff --git a/tests/cefclient/cefclient.vsprops b/tests/cefclient/cefclient.vsprops index 77cfb5ac9..366681a54 100644 --- a/tests/cefclient/cefclient.vsprops +++ b/tests/cefclient/cefclient.vsprops @@ -12,7 +12,7 @@ /> (instance->pdata); @@ -33,6 +34,7 @@ NPError NPP_Destroy(NPP instance, NPSavedData** save) { return NPERR_NO_ERROR; } +static NPError NPP_SetWindow(NPP instance, NPWindow* window_info) { if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; diff --git a/tests/cefclient/res/logoball.png b/tests/cefclient/res/logoball.png new file mode 100644 index 0000000000000000000000000000000000000000..ef115ca070a45aa14a22b8924c47bae29f02b2ca GIT binary patch literal 7181 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 + +User Interface App Example + + +
+ + + + +
+ + + + + + + + + + + + + + +
User Interface App Example
An embedded OpenGL plugin window that communicates with the Chromium browser control via JavaScript calls. + View Page Source
You can make the square rotate! +
    +
  • Click the square with the left mouse button or click the Decrement Rotation button to decrement the rotation value.
  • +
  • Click the square with the right mouse button or click the Increment Rotation button to increment the rotation value.
  • +
  • Click the Reset Rotation button to reset the rotation value to zero.
  • +
+
+ +
+ +
+ +
+ Rotation Value: +
+ + +
+
+
+ + + diff --git a/tests/cefclient/resource.h b/tests/cefclient/resource.h index 44dad4cd6..0e8fc8fb4 100644 --- a/tests/cefclient/resource.h +++ b/tests/cefclient/resource.h @@ -28,8 +28,11 @@ #define ID_TESTS_POPUP 32775 #define ID_TESTS_REQUEST 32776 #define ID_TESTS_SCHEME_HANDLER 32777 +#define ID_TESTS_UIAPP 32778 #define IDC_STATIC -1 #define IDS_LOGO 1000 +#define IDS_UIPLUGIN 1001 +#define IDS_LOGOBALL 1002 // Next default values for new objects // diff --git a/tests/cefclient/uiplugin.cpp b/tests/cefclient/uiplugin.cpp new file mode 100644 index 000000000..721db7142 --- /dev/null +++ b/tests/cefclient/uiplugin.cpp @@ -0,0 +1,284 @@ +// Copyright (c) 2009 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium 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 "stdafx.h" +#include "uiplugin.h" +#include "cefclient.h" +#include +#include + + +// Initialized in NP_Initialize. +NPNetscapeFuncs* g_uibrowser = NULL; + +// Global values. +float g_rotationspeed = 0.0f; +float g_theta = 0.0f; + +// Class holding pointers for the client plugin window. +class ClientPlugin +{ +public: + ClientPlugin() + { + hWnd = NULL; + hDC = NULL; + hRC = NULL; + } + + HWND hWnd; + HDC hDC; + HGLRC hRC; +}; + +// Forward declarations of functions included in this code module: +LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam); +void EnableOpenGL(HWND hWnd, HDC * hDC, HGLRC * hRC); +void DisableOpenGL(HWND hWnd, HDC hDC, HGLRC hRC); + +static +NPError NPP_New(NPMIMEType plugin_type, NPP instance, uint16 mode, int16 argc, + char* argn[], char* argv[], NPSavedData* saved) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + ClientPlugin *plugin = new ClientPlugin; + instance->pdata = reinterpret_cast(plugin); + + return NPERR_NO_ERROR; +} + +static +NPError NPP_Destroy(NPP instance, NPSavedData** save) { + ClientPlugin *plugin = reinterpret_cast(instance->pdata); + + if (plugin) { + if(plugin->hWnd) { + DestroyWindow(plugin->hWnd); + DisableOpenGL(plugin->hWnd, plugin->hDC, plugin->hRC); + } + delete plugin; + g_rotationspeed = 0.0f; + g_theta = 0.0f; + } + + return NPERR_NO_ERROR; +} + +static +NPError NPP_SetWindow(NPP instance, NPWindow* window_info) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + if (window_info == NULL) + return NPERR_GENERIC_ERROR; + + ClientPlugin *plugin = reinterpret_cast(instance->pdata); + HWND parent_hwnd = reinterpret_cast(window_info->window); + + if (plugin->hWnd == NULL) + { + WNDCLASS wc; + HINSTANCE hInstance = GetModuleHandle(NULL); + + // Register the window class. + wc.style = CS_OWNDC; + wc.lpfnWndProc = PluginWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = L"ClientUIPlugin"; + RegisterClass(&wc); + + // Create the main window. + plugin->hWnd = CreateWindow(L"ClientUIPlugin", L"Client UI Plugin", + WS_CHILD, 0, 0, 0, 0, parent_hwnd, NULL, hInstance, NULL); + + // Enable OpenGL drawing for the window. + EnableOpenGL(plugin->hWnd, &(plugin->hDC), &(plugin->hRC)); + } + + // Position the window and make sure it's visible. + RECT parent_rect; + GetClientRect(parent_hwnd, &parent_rect); + SetWindowPos(plugin->hWnd, NULL, parent_rect.left, parent_rect.top, + parent_rect.right - parent_rect.left, + parent_rect.bottom - parent_rect.top, SWP_SHOWWINDOW); + + UpdateWindow(plugin->hWnd); + ShowWindow(plugin->hWnd, SW_SHOW); + + return NPERR_NO_ERROR; +} + +NPError API_CALL NP_UIGetEntryPoints(NPPluginFuncs* pFuncs) +{ + pFuncs->newp = NPP_New; + pFuncs->destroy = NPP_Destroy; + pFuncs->setwindow = NPP_SetWindow; + return NPERR_NO_ERROR; +} + +NPError API_CALL NP_UIInitialize(NPNetscapeFuncs* pFuncs) +{ + g_uibrowser = pFuncs; + return NPERR_NO_ERROR; +} + +NPError API_CALL NP_UIShutdown(void) +{ + g_uibrowser = NULL; + return NPERR_NO_ERROR; +} + +// Send the notification to the browser as a JavaScript function call. +static void NotifyNewRotation(float value) +{ + std::wstringstream buf; + buf << L"notifyNewRotation(" << value << L");"; + AppGetBrowser()->GetMainFrame()->ExecuteJavaScript(buf.str(), std::wstring(), + 0); +} + +void ModifyRotation(float value) +{ + g_rotationspeed += value; + NotifyNewRotation(g_rotationspeed); +} + +void ResetRotation() +{ + g_rotationspeed = 0.0; + NotifyNewRotation(g_rotationspeed); +} + +// Nice little fly polygon borrowed from the OpenGL Red Book. +const GLubyte fly[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x80, 0x01, 0xC0, 0x06, 0xC0, 0x03, 0x60, + 0x04, 0x60, 0x06, 0x20, 0x04, 0x30, 0x0C, 0x20, + 0x04, 0x18, 0x18, 0x20, 0x04, 0x0C, 0x30, 0x20, + 0x04, 0x06, 0x60, 0x20, 0x44, 0x03, 0xC0, 0x22, + 0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22, + 0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22, + 0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22, + 0x66, 0x01, 0x80, 0x66, 0x33, 0x01, 0x80, 0xCC, + 0x19, 0x81, 0x81, 0x98, 0x0C, 0xC1, 0x83, 0x30, + 0x07, 0xe1, 0x87, 0xe0, 0x03, 0x3f, 0xfc, 0xc0, + 0x03, 0x31, 0x8c, 0xc0, 0x03, 0x33, 0xcc, 0xc0, + 0x06, 0x64, 0x26, 0x60, 0x0c, 0xcc, 0x33, 0x30, + 0x18, 0xcc, 0x33, 0x18, 0x10, 0xc4, 0x23, 0x08, + 0x10, 0x63, 0xC6, 0x08, 0x10, 0x30, 0x0c, 0x08, + 0x10, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x08}; + + +// Plugin window procedure. +LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam) +{ + HDC hDC; + int width, height; + + switch(message) + { + case WM_CREATE: + // Start the timer that's used for redrawing. + SetTimer(hWnd, 1, 1, NULL); + return 0; + + case WM_DESTROY: + // Stop the timer that's used for redrawing. + KillTimer(hWnd, 1); + return 0; + + case WM_LBUTTONDOWN: + // Decrement rotation speed. + ModifyRotation(-2.0f); + return 0; + + case WM_RBUTTONDOWN: + // Increment rotation speed. + ModifyRotation(2.0f); + return 0; + + case WM_SIZE: + // Resize the OpenGL viewport to match the window size. + width = LOWORD(lParam); + height = HIWORD(lParam); + glViewport(0, 0, width, height); + break; + + case WM_ERASEBKGND: + return 0; + + case WM_TIMER: + // Adjust the theta value and redraw the display when the timer fires. + hDC = GetDC(hWnd); + + glClearColor(1.0f, 1.0f, 1.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + glPushMatrix(); + glEnable(GL_POLYGON_STIPPLE); + glPolygonStipple(fly); + + glRotatef(g_theta, 0.0f, 0.0f, 1.0f); + glBegin(GL_QUADS); + glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(0.7f, 0.7f); + glColor3f(0.0f, 1.0f, 0.0f); glVertex2f(0.7f, -0.7f); + glColor3f(0.0f, 0.0f, 1.0f); glVertex2f(-0.7f, -0.7f); + glColor3f(1.0f, 0.0f, 1.0f); glVertex2f(-0.7f, 0.7f); + glEnd(); + + glDisable(GL_POLYGON_STIPPLE); + glPopMatrix(); + + SwapBuffers(hDC); + ReleaseDC(hWnd, hDC); + + g_theta -= g_rotationspeed; + } + + return DefWindowProc(hWnd, message, wParam, lParam); +} + +// Enable OpenGL. +void EnableOpenGL(HWND hWnd, HDC * hDC, HGLRC * hRC) +{ + PIXELFORMATDESCRIPTOR pfd; + int format; + + // Get the device context. + *hDC = GetDC(hWnd); + + // Set the pixel format for the DC. + ZeroMemory(&pfd, sizeof(pfd)); + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 16; + pfd.iLayerType = PFD_MAIN_PLANE; + format = ChoosePixelFormat(*hDC, &pfd); + SetPixelFormat(*hDC, format, &pfd); + + // Create and enable the render contex. + *hRC = wglCreateContext(*hDC); + wglMakeCurrent(*hDC, *hRC); +} + +// Disable OpenGL. +void DisableOpenGL(HWND hWnd, HDC hDC, HGLRC hRC) +{ + wglMakeCurrent(NULL, NULL); + wglDeleteContext(hRC); + ReleaseDC(hWnd, hDC); +} diff --git a/tests/cefclient/uiplugin.h b/tests/cefclient/uiplugin.h new file mode 100644 index 000000000..b2063db81 --- /dev/null +++ b/tests/cefclient/uiplugin.h @@ -0,0 +1,22 @@ +// Copyright (c) 2009 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Portions of this implementation are borrowed from webkit\default_plugin\ +// plugin_impl.h + +#pragma once + +#include "cef_nplugin.h" + +extern NPNetscapeFuncs* g_uibrowser; + +NPError API_CALL NP_UIGetEntryPoints(NPPluginFuncs* pFuncs); +NPError API_CALL NP_UIInitialize(NPNetscapeFuncs* pFuncs); +NPError API_CALL NP_UIShutdown(void); + +// Function called to modify the rotation value. +void ModifyRotation(float value); +// Function called to reset the rotation value. +void ResetRotation(); \ No newline at end of file diff --git a/tests/cefclient/uiplugin_test.cpp b/tests/cefclient/uiplugin_test.cpp new file mode 100644 index 000000000..610e02859 --- /dev/null +++ b/tests/cefclient/uiplugin_test.cpp @@ -0,0 +1,109 @@ +// Copyright (c) 2008-2009 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 "stdafx.h" +#include "uiplugin_test.h" +#include "uiplugin.h" +#include "cefclient.h" + + +void InitUIPluginTest() +{ + // Structure providing information about the client plugin. + CefPluginInfo plugin_info; + plugin_info.display_name = L"Client UI Plugin"; + plugin_info.unique_name = L"client_ui_plugin"; + plugin_info.version = L"1, 0, 0, 1"; + plugin_info.description = L"My Example Client UI Plugin"; + + CefPluginMimeType mime_type; + mime_type.mime_type = L"application/x-client-ui-plugin"; + mime_type.file_extensions.push_back(L"*"); + plugin_info.mime_types.push_back(mime_type); + + plugin_info.np_getentrypoints = NP_UIGetEntryPoints; + plugin_info.np_initialize = NP_UIInitialize; + plugin_info.np_shutdown = NP_UIShutdown; + + // Register the internal client plugin + CefRegisterPlugin(plugin_info); +} + +// Implementation of the V8 handler class for the "window.uiapp" functions. +class ClientV8UIHandler : public CefThreadSafeBase +{ +public: + ClientV8UIHandler() {} + + // Execute with the specified argument list and return value. Return true if + // the method was handled. + virtual bool Execute(const std::wstring& name, + CefRefPtr object, + const CefV8ValueList& arguments, + CefRefPtr& retval, + std::wstring& exception) + { + if(name == L"modifyRotation") { + // This function requires one argument. + if(arguments.size() != 1) + return false; + + float increment = 0.; + if(arguments[0]->IsInt()) { + // The argument is an integer value. + increment = static_cast(arguments[0]->GetIntValue()); + } else if(arguments[0]->IsDouble()) { + // The argument is an double value. + increment = static_cast(arguments[0]->GetDoubleValue()); + } + + if(increment != 0.) { + // Modify the rotation accordingly. + ModifyRotation(increment); + return true; + } + } else if(name == L"resetRotation") { + // Reset the rotation value. + ResetRotation(); + return true; + } else if(name == L"viewSource") { + // View the page source. + AppGetBrowser()->GetMainFrame()->ViewSource(); + return true; + } + + return false; + } +}; + +void InitUIBindingTest(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr object) +{ + if(frame->GetURL().find(L"http://tests/uiapp") != 0) + return; + + // Create the new V8 object. + CefRefPtr testObjPtr = CefV8Value::CreateObject(NULL); + // Add the new V8 object to the global window object with the name "uiapp". + object->SetValue(L"uiapp", testObjPtr); + + // Create an instance of ClientV8UIHandler as the V8 handler. + CefRefPtr handlerPtr = new ClientV8UIHandler(); + + // Add a new V8 function to the uiapp object with the name "modifyRotation". + testObjPtr->SetValue(L"modifyRotation", + CefV8Value::CreateFunction(L"modifyRotation", handlerPtr)); + // Add a new V8 function to the uiapp object with the name "resetRotation". + testObjPtr->SetValue(L"resetRotation", + CefV8Value::CreateFunction(L"resetRotation", handlerPtr)); + // Add a new V8 function to the uiapp object with the name "viewSource". + testObjPtr->SetValue(L"viewSource", + CefV8Value::CreateFunction(L"viewSource", handlerPtr)); +} + +void RunUIPluginTest(CefRefPtr browser) +{ + browser->GetMainFrame()->LoadURL(L"http://tests/uiapp"); +} diff --git a/tests/cefclient/uiplugin_test.h b/tests/cefclient/uiplugin_test.h new file mode 100644 index 000000000..2cbabd8e0 --- /dev/null +++ b/tests/cefclient/uiplugin_test.h @@ -0,0 +1,18 @@ +// Copyright (c) 2009 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. + +#pragma once +#include "cef.h" + +// Register the internal client plugin. +void InitUIPluginTest(); + +// Add the V8 bindings. +void InitUIBindingTest(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr object); + + +// Run the test. +void RunUIPluginTest(CefRefPtr browser);