diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/plugins/win | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/plugins/win')
-rw-r--r-- | Source/WebCore/plugins/win/PaintHooks.asm | 50 | ||||
-rw-r--r-- | Source/WebCore/plugins/win/PluginDatabaseWin.cpp | 451 | ||||
-rw-r--r-- | Source/WebCore/plugins/win/PluginMessageThrottlerWin.cpp | 151 | ||||
-rw-r--r-- | Source/WebCore/plugins/win/PluginMessageThrottlerWin.h | 74 | ||||
-rw-r--r-- | Source/WebCore/plugins/win/PluginPackageWin.cpp | 342 | ||||
-rw-r--r-- | Source/WebCore/plugins/win/PluginViewWin.cpp | 1080 |
6 files changed, 2148 insertions, 0 deletions
diff --git a/Source/WebCore/plugins/win/PaintHooks.asm b/Source/WebCore/plugins/win/PaintHooks.asm new file mode 100644 index 0000000..1508813 --- /dev/null +++ b/Source/WebCore/plugins/win/PaintHooks.asm @@ -0,0 +1,50 @@ +;/* +; Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) +; +; This library is free software; you can redistribute it and/or +; modify it under the terms of the GNU Library General Public +; License as published by the Free Software Foundation; either +; version 2 of the License, or (at your option) any later version. +; +; This library 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 +; Library General Public License for more details. +; +; You should have received a copy of the GNU Library General Public License +; along with this library; see the file COPYING.LIB. If not, write to +; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +; Boston, MA 02110-1301, USA. +;*/ + +;HDC __stdcall _HBeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint); + +PUBLIC _HBeginPaint + +_TEXT SEGMENT + +_HBeginPaint PROC + mov r10,rcx + mov eax,1017h + syscall + ret +_HBeginPaint ENDP + +_TEXT ENDS + +;BOOL __stdcall _HEndPaint(HWND hWnd, const PAINTSTRUCT* lpPaint); + +PUBLIC _HEndPaint + +_TEXT SEGMENT + +_HEndPaint PROC + mov r10,rcx + mov eax,1019h + syscall + ret +_HEndPaint ENDP + +_TEXT ENDS + +END diff --git a/Source/WebCore/plugins/win/PluginDatabaseWin.cpp b/Source/WebCore/plugins/win/PluginDatabaseWin.cpp new file mode 100644 index 0000000..27121c6 --- /dev/null +++ b/Source/WebCore/plugins/win/PluginDatabaseWin.cpp @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * Copyright (C) 2008-2009 Torch Mobile, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginDatabase.h" + +#include "Frame.h" +#include "KURL.h" +#include "PluginPackage.h" +#include <windows.h> +#include <shlwapi.h> + +#if OS(WINCE) +// WINCE doesn't support Registry Key Access Rights. The parameter should always be 0 +#ifndef KEY_ENUMERATE_SUB_KEYS +#define KEY_ENUMERATE_SUB_KEYS 0 +#endif + +DWORD SHGetValue(HKEY hkey, LPCWSTR pszSubKey, LPCWSTR pszValue, LPDWORD pdwType, LPVOID pvData, LPDWORD pcbData) +{ + HKEY key; + if (RegOpenKeyEx(hkey, pszSubKey, 0, 0, &key) == ERROR_SUCCESS) { + DWORD result = RegQueryValueEx(key, pszValue, 0, pdwType, (LPBYTE)pvData, pcbData); + RegCloseKey(key); + return result; + } + return ERROR_INVALID_NAME; +} + +BOOL PathRemoveFileSpec(LPWSTR moduleFileNameStr) +{ + if (!*moduleFileNameStr) + return FALSE; + + LPWSTR lastPos = 0; + LPWSTR curPos = moduleFileNameStr; + do { + if (*curPos == L'/' || *curPos == L'\\') + lastPos = curPos; + } while (*++curPos); + + if (lastPos == curPos - 1) + return FALSE; + + if (lastPos) + *lastPos = 0; + else { + moduleFileNameStr[0] = L'\\'; + moduleFileNameStr[1] = 0; + } + + return TRUE; +} +#endif + +namespace WebCore { + +static inline void addPluginPathsFromRegistry(HKEY rootKey, HashSet<String>& paths) +{ + HKEY key; + HRESULT result = RegOpenKeyExW(rootKey, L"Software\\MozillaPlugins", 0, KEY_ENUMERATE_SUB_KEYS, &key); + + if (result != ERROR_SUCCESS) + return; + + wchar_t name[128]; + FILETIME lastModified; + + // Enumerate subkeys + for (int i = 0;; i++) { + DWORD nameLen = WTF_ARRAY_LENGTH(name); + result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified); + + if (result != ERROR_SUCCESS) + break; + + WCHAR pathStr[_MAX_PATH]; + DWORD pathStrSize = sizeof(pathStr); + DWORD type; + + result = SHGetValue(key, name, TEXT("Path"), &type, (LPBYTE)pathStr, &pathStrSize); + if (result != ERROR_SUCCESS || type != REG_SZ) + continue; + + paths.add(String(pathStr, pathStrSize / sizeof(WCHAR) - 1)); + } + + RegCloseKey(key); +} + +void PluginDatabase::getPluginPathsInDirectories(HashSet<String>& paths) const +{ + // FIXME: This should be a case insensitive set. + HashSet<String> uniqueFilenames; + + HANDLE hFind = INVALID_HANDLE_VALUE; + WIN32_FIND_DATAW findFileData; + + String oldWMPPluginPath; + String newWMPPluginPath; + + Vector<String>::const_iterator end = m_pluginDirectories.end(); + for (Vector<String>::const_iterator it = m_pluginDirectories.begin(); it != end; ++it) { + String pattern = *it + "\\*"; + + hFind = FindFirstFileW(pattern.charactersWithNullTermination(), &findFileData); + + if (hFind == INVALID_HANDLE_VALUE) + continue; + + do { + if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + continue; + + String filename = String(findFileData.cFileName, wcslen(findFileData.cFileName)); + if ((!filename.startsWith("np", false) || !filename.endsWith("dll", false)) && + (!equalIgnoringCase(filename, "Plugin.dll") || !it->endsWith("Shockwave 10", false))) + continue; + + String fullPath = *it + "\\" + filename; + if (!uniqueFilenames.add(fullPath).second) + continue; + + paths.add(fullPath); + + if (equalIgnoringCase(filename, "npdsplay.dll")) + oldWMPPluginPath = fullPath; + else if (equalIgnoringCase(filename, "np-mswmp.dll")) + newWMPPluginPath = fullPath; + + } while (FindNextFileW(hFind, &findFileData) != 0); + + FindClose(hFind); + } + + addPluginPathsFromRegistry(HKEY_LOCAL_MACHINE, paths); + addPluginPathsFromRegistry(HKEY_CURRENT_USER, paths); + + // If both the old and new WMP plugin are present in the plugins set, + // we remove the old one so we don't end up choosing the old one. + if (!oldWMPPluginPath.isEmpty() && !newWMPPluginPath.isEmpty()) + paths.remove(oldWMPPluginPath); +} + +static inline Vector<int> parseVersionString(const String& versionString) +{ + Vector<int> version; + + unsigned startPos = 0; + unsigned endPos; + + while (startPos < versionString.length()) { + for (endPos = startPos; endPos < versionString.length(); ++endPos) + if (versionString[endPos] == '.' || versionString[endPos] == '_') + break; + + int versionComponent = versionString.substring(startPos, endPos - startPos).toInt(); + version.append(versionComponent); + + startPos = endPos + 1; + } + + return version; +} + +// This returns whether versionA is higher than versionB +static inline bool compareVersions(const Vector<int>& versionA, const Vector<int>& versionB) +{ + for (unsigned i = 0; i < versionA.size(); i++) { + if (i >= versionB.size()) + return true; + + if (versionA[i] > versionB[i]) + return true; + else if (versionA[i] < versionB[i]) + return false; + } + + // If we come here, the versions are either the same or versionB has an extra component, just return false + return false; +} + +static inline void addMozillaPluginDirectories(Vector<String>& directories) +{ + // Enumerate all Mozilla plugin directories in the registry + HKEY key; + LONG result; + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Mozilla"), 0, KEY_READ, &key); + if (result == ERROR_SUCCESS) { + WCHAR name[128]; + FILETIME lastModified; + + // Enumerate subkeys + for (int i = 0;; i++) { + DWORD nameLen = sizeof(name) / sizeof(WCHAR); + result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified); + + if (result != ERROR_SUCCESS) + break; + + String extensionsPath = String(name, nameLen) + "\\Extensions"; + HKEY extensionsKey; + + // Try opening the key + result = RegOpenKeyEx(key, extensionsPath.charactersWithNullTermination(), 0, KEY_READ, &extensionsKey); + + if (result == ERROR_SUCCESS) { + // Now get the plugins directory + WCHAR pluginsDirectoryStr[_MAX_PATH]; + DWORD pluginsDirectorySize = sizeof(pluginsDirectoryStr); + DWORD type; + + result = RegQueryValueEx(extensionsKey, TEXT("Plugins"), 0, &type, (LPBYTE)&pluginsDirectoryStr, &pluginsDirectorySize); + + if (result == ERROR_SUCCESS && type == REG_SZ) + directories.append(String(pluginsDirectoryStr, pluginsDirectorySize / sizeof(WCHAR) - 1)); + + RegCloseKey(extensionsKey); + } + } + + RegCloseKey(key); + } +} + +static inline void addWindowsMediaPlayerPluginDirectory(Vector<String>& directories) +{ +#if !OS(WINCE) + // The new WMP Firefox plugin is installed in \PFiles\Plugins if it can't find any Firefox installs + WCHAR pluginDirectoryStr[_MAX_PATH + 1]; + DWORD pluginDirectorySize = ::ExpandEnvironmentStringsW(TEXT("%SYSTEMDRIVE%\\PFiles\\Plugins"), pluginDirectoryStr, WTF_ARRAY_LENGTH(pluginDirectoryStr)); + + if (pluginDirectorySize > 0 && pluginDirectorySize <= WTF_ARRAY_LENGTH(pluginDirectoryStr)) + directories.append(String(pluginDirectoryStr, pluginDirectorySize - 1)); +#endif + + DWORD type; + WCHAR installationDirectoryStr[_MAX_PATH]; + DWORD installationDirectorySize = sizeof(installationDirectoryStr); + + HRESULT result = SHGetValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\MediaPlayer"), TEXT("Installation Directory"), &type, (LPBYTE)&installationDirectoryStr, &installationDirectorySize); + + if (result == ERROR_SUCCESS && type == REG_SZ) + directories.append(String(installationDirectoryStr, installationDirectorySize / sizeof(WCHAR) - 1)); +} + +static inline void addQuickTimePluginDirectory(Vector<String>& directories) +{ + DWORD type; + WCHAR installationDirectoryStr[_MAX_PATH]; + DWORD installationDirectorySize = sizeof(installationDirectoryStr); + + HRESULT result = SHGetValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Apple Computer, Inc.\\QuickTime"), TEXT("InstallDir"), &type, (LPBYTE)&installationDirectoryStr, &installationDirectorySize); + + if (result == ERROR_SUCCESS && type == REG_SZ) { + String pluginDir = String(installationDirectoryStr, installationDirectorySize / sizeof(WCHAR) - 1) + "\\plugins"; + directories.append(pluginDir); + } +} + +static inline void addAdobeAcrobatPluginDirectory(Vector<String>& directories) +{ + HKEY key; + HRESULT result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Adobe\\Acrobat Reader"), 0, KEY_READ, &key); + if (result != ERROR_SUCCESS) + return; + + WCHAR name[128]; + FILETIME lastModified; + + Vector<int> latestAcrobatVersion; + String latestAcrobatVersionString; + + // Enumerate subkeys + for (int i = 0;; i++) { + DWORD nameLen = sizeof(name) / sizeof(WCHAR); + result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified); + + if (result != ERROR_SUCCESS) + break; + + Vector<int> acrobatVersion = parseVersionString(String(name, nameLen)); + if (compareVersions(acrobatVersion, latestAcrobatVersion)) { + latestAcrobatVersion = acrobatVersion; + latestAcrobatVersionString = String(name, nameLen); + } + } + + if (!latestAcrobatVersionString.isNull()) { + DWORD type; + WCHAR acrobatInstallPathStr[_MAX_PATH]; + DWORD acrobatInstallPathSize = sizeof(acrobatInstallPathStr); + + String acrobatPluginKeyPath = "Software\\Adobe\\Acrobat Reader\\" + latestAcrobatVersionString + "\\InstallPath"; + result = SHGetValue(HKEY_LOCAL_MACHINE, acrobatPluginKeyPath.charactersWithNullTermination(), 0, &type, (LPBYTE)acrobatInstallPathStr, &acrobatInstallPathSize); + + if (result == ERROR_SUCCESS) { + String acrobatPluginDirectory = String(acrobatInstallPathStr, acrobatInstallPathSize / sizeof(WCHAR) - 1) + "\\browser"; + directories.append(acrobatPluginDirectory); + } + } + + RegCloseKey(key); +} + +static inline void addJavaPluginDirectory(Vector<String>& directories) +{ + HKEY key; + HRESULT result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\JavaSoft\\Java Plug-in"), 0, KEY_READ, &key); + if (result != ERROR_SUCCESS) + return; + + WCHAR name[128]; + FILETIME lastModified; + + Vector<int> latestJavaVersion; + String latestJavaVersionString; + + // Enumerate subkeys + for (int i = 0;; i++) { + DWORD nameLen = sizeof(name) / sizeof(WCHAR); + result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified); + + if (result != ERROR_SUCCESS) + break; + + Vector<int> javaVersion = parseVersionString(String(name, nameLen)); + if (compareVersions(javaVersion, latestJavaVersion)) { + latestJavaVersion = javaVersion; + latestJavaVersionString = String(name, nameLen); + } + } + + if (!latestJavaVersionString.isEmpty()) { + DWORD type; + WCHAR javaInstallPathStr[_MAX_PATH]; + DWORD javaInstallPathSize = sizeof(javaInstallPathStr); + DWORD useNewPluginValue; + DWORD useNewPluginSize; + + String javaPluginKeyPath = "Software\\JavaSoft\\Java Plug-in\\" + latestJavaVersionString; + result = SHGetValue(HKEY_LOCAL_MACHINE, javaPluginKeyPath.charactersWithNullTermination(), TEXT("UseNewJavaPlugin"), &type, (LPVOID)&useNewPluginValue, &useNewPluginSize); + + if (result == ERROR_SUCCESS && useNewPluginValue == 1) { + result = SHGetValue(HKEY_LOCAL_MACHINE, javaPluginKeyPath.charactersWithNullTermination(), TEXT("JavaHome"), &type, (LPBYTE)javaInstallPathStr, &javaInstallPathSize); + if (result == ERROR_SUCCESS) { + String javaPluginDirectory = String(javaInstallPathStr, javaInstallPathSize / sizeof(WCHAR) - 1) + "\\bin\\new_plugin"; + directories.append(javaPluginDirectory); + } + } + } + + RegCloseKey(key); +} + +static inline String safariPluginsDirectory() +{ + WCHAR moduleFileNameStr[_MAX_PATH]; + static String pluginsDirectory; + static bool cachedPluginDirectory = false; + + if (!cachedPluginDirectory) { + cachedPluginDirectory = true; + + int moduleFileNameLen = GetModuleFileName(0, moduleFileNameStr, _MAX_PATH); + + if (!moduleFileNameLen || moduleFileNameLen == _MAX_PATH) + goto exit; + + if (!PathRemoveFileSpec(moduleFileNameStr)) + goto exit; + + pluginsDirectory = String(moduleFileNameStr) + "\\Plugins"; + } +exit: + return pluginsDirectory; +} + +static inline void addMacromediaPluginDirectories(Vector<String>& directories) +{ +#if !OS(WINCE) + WCHAR systemDirectoryStr[MAX_PATH]; + + if (!GetSystemDirectory(systemDirectoryStr, WTF_ARRAY_LENGTH(systemDirectoryStr))) + return; + + WCHAR macromediaDirectoryStr[MAX_PATH]; + + PathCombine(macromediaDirectoryStr, systemDirectoryStr, TEXT("macromed\\Flash")); + directories.append(macromediaDirectoryStr); + + PathCombine(macromediaDirectoryStr, systemDirectoryStr, TEXT("macromed\\Shockwave 10")); + directories.append(macromediaDirectoryStr); +#endif +} + +Vector<String> PluginDatabase::defaultPluginDirectories() +{ + Vector<String> directories; + String ourDirectory = safariPluginsDirectory(); + + if (!ourDirectory.isNull()) + directories.append(ourDirectory); + addQuickTimePluginDirectory(directories); + addAdobeAcrobatPluginDirectory(directories); + addMozillaPluginDirectories(directories); + addWindowsMediaPlayerPluginDirectory(directories); + addMacromediaPluginDirectories(directories); +#if PLATFORM(QT) + addJavaPluginDirectory(directories); +#endif + + return directories; +} + +bool PluginDatabase::isPreferredPluginDirectory(const String& directory) +{ + String ourDirectory = safariPluginsDirectory(); + + if (!ourDirectory.isNull() && !directory.isNull()) + return ourDirectory == directory; + + return false; +} + +} diff --git a/Source/WebCore/plugins/win/PluginMessageThrottlerWin.cpp b/Source/WebCore/plugins/win/PluginMessageThrottlerWin.cpp new file mode 100644 index 0000000..c5f3081 --- /dev/null +++ b/Source/WebCore/plugins/win/PluginMessageThrottlerWin.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginMessageThrottlerWin.h" + +#include "PluginView.h" +#include <wtf/ASCIICType.h> +#include <wtf/CurrentTime.h> + +using namespace WTF; + +namespace WebCore { + +// Set a timer to make sure we process any queued messages at least every 16ms. +// This value allows Flash 60 messages/second, which should be enough for video +// playback, and also gets us over the limit for kicking into high-resolution +// timer mode (see SharedTimerWin.cpp). +static const double MessageThrottleTimeInterval = 0.016; + +// During a continuous stream of messages, process one every 5ms. +static const double MessageDirectProcessingInterval = 0.005; + +PluginMessageThrottlerWin::PluginMessageThrottlerWin(PluginView* pluginView) + : m_pluginView(pluginView) + , m_back(0) + , m_front(0) + , m_messageThrottleTimer(this, &PluginMessageThrottlerWin::messageThrottleTimerFired) + , m_lastMessageTime(0) +{ + // Initialize the free list with our inline messages + for (unsigned i = 0; i < NumInlineMessages - 1; i++) + m_inlineMessages[i].next = &m_inlineMessages[i + 1]; + m_inlineMessages[NumInlineMessages - 1].next = 0; + m_freeInlineMessages = &m_inlineMessages[0]; +} + +PluginMessageThrottlerWin::~PluginMessageThrottlerWin() +{ + PluginMessage* next; + + for (PluginMessage* message = m_front; message; message = next) { + next = message->next; + freeMessage(message); + } +} + +void PluginMessageThrottlerWin::appendMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + PluginMessage* message = allocateMessage(); + + message->hWnd = hWnd; + message->msg = msg; + message->wParam = wParam; + message->lParam = lParam; + message->next = 0; + + if (m_back) + m_back->next = message; + m_back = message; + if (!m_front) + m_front = message; + + // If it has been more than MessageDirectProcessingInterval between throttled messages, + // go ahead and process a message directly. + double currentTime = WTF::currentTime(); + if (currentTime - m_lastMessageTime > MessageDirectProcessingInterval) { + processQueuedMessage(); + m_lastMessageTime = currentTime; + if (!m_front) + return; + } + + if (!m_messageThrottleTimer.isActive()) + m_messageThrottleTimer.startOneShot(MessageThrottleTimeInterval); +} + +void PluginMessageThrottlerWin::processQueuedMessage() +{ + PluginMessage* message = m_front; + m_front = m_front->next; + if (message == m_back) + m_back = 0; + + // Protect the PluginView from destruction while calling its window proc. + // <rdar://problem/6930280> + RefPtr<PluginView> protect(m_pluginView); + ::CallWindowProc(m_pluginView->pluginWndProc(), message->hWnd, message->msg, message->wParam, message->lParam); + + freeMessage(message); +} + +void PluginMessageThrottlerWin::messageThrottleTimerFired(Timer<PluginMessageThrottlerWin>*) +{ + processQueuedMessage(); + + if (m_front) + m_messageThrottleTimer.startOneShot(MessageThrottleTimeInterval); +} + +PluginMessage* PluginMessageThrottlerWin::allocateMessage() +{ + PluginMessage *message; + + if (m_freeInlineMessages) { + message = m_freeInlineMessages; + m_freeInlineMessages = message->next; + } else + message = new PluginMessage; + + return message; +} + +bool PluginMessageThrottlerWin::isInlineMessage(PluginMessage* message) +{ + return message >= &m_inlineMessages[0] && message <= &m_inlineMessages[NumInlineMessages - 1]; +} + +void PluginMessageThrottlerWin::freeMessage(PluginMessage* message) +{ + if (isInlineMessage(message)) { + message->next = m_freeInlineMessages; + m_freeInlineMessages = message; + } else + delete message; +} + +} // namespace WebCore diff --git a/Source/WebCore/plugins/win/PluginMessageThrottlerWin.h b/Source/WebCore/plugins/win/PluginMessageThrottlerWin.h new file mode 100644 index 0000000..0a7be70 --- /dev/null +++ b/Source/WebCore/plugins/win/PluginMessageThrottlerWin.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PluginMessageThrottlerWin_h +#define PluginMessageThrottlerWin_h + +#include "Timer.h" + +#include <windows.h> + +namespace WebCore { + class PluginView; + + struct PluginMessage { + HWND hWnd; + UINT msg; + WPARAM wParam; + LPARAM lParam; + + struct PluginMessage* next; + }; + + class PluginMessageThrottlerWin { + public: + PluginMessageThrottlerWin(PluginView*); + ~PluginMessageThrottlerWin(); + + void appendMessage(HWND, UINT msg, WPARAM, LPARAM); + + private: + void processQueuedMessage(); + void messageThrottleTimerFired(Timer<PluginMessageThrottlerWin>*); + PluginMessage* allocateMessage(); + bool isInlineMessage(PluginMessage* message); + void freeMessage(PluginMessage* message); + + PluginView* m_pluginView; + PluginMessage* m_back; + PluginMessage* m_front; + + static const int NumInlineMessages = 4; + PluginMessage m_inlineMessages[NumInlineMessages]; + PluginMessage* m_freeInlineMessages; + + Timer<PluginMessageThrottlerWin> m_messageThrottleTimer; + double m_lastMessageTime; + }; + +} // namespace WebCore + +#endif // PluginMessageThrottlerWin_h diff --git a/Source/WebCore/plugins/win/PluginPackageWin.cpp b/Source/WebCore/plugins/win/PluginPackageWin.cpp new file mode 100644 index 0000000..74bd2a9 --- /dev/null +++ b/Source/WebCore/plugins/win/PluginPackageWin.cpp @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * Copyright (C) 2009 Torch Mobile, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "config.h" +#include "PluginPackage.h" + +#include "MIMETypeRegistry.h" +#include "PluginDatabase.h" +#include "PluginDebug.h" +#include "Timer.h" +#include "npruntime_impl.h" +#include <string.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/text/CString.h> +#include <shlwapi.h> + +namespace WebCore { + +static String getVersionInfo(const LPVOID versionInfoData, const String& info) +{ + LPVOID buffer; + UINT bufferLength; + String subInfo = "\\StringfileInfo\\040904E4\\" + info; + bool retval = VerQueryValueW(versionInfoData, + const_cast<UChar*>(subInfo.charactersWithNullTermination()), + &buffer, &bufferLength); + if (!retval || bufferLength == 0) + return String(); + + // Subtract 1 from the length; we don't want the trailing null character. + return String(reinterpret_cast<UChar*>(buffer), bufferLength - 1); +} + +bool PluginPackage::isPluginBlacklisted() +{ + if (name() == "Citrix ICA Client") { + // The Citrix ICA Client plug-in requires a Mozilla-based browser; see <rdar://6418681>. + return true; + } + + if (name() == "Silverlight Plug-In") { + // workaround for <rdar://5557379> Crash in Silverlight when opening microsoft.com. + // the latest 1.0 version of Silverlight does not reproduce this crash, so allow it + // and any newer versions + static const PlatformModuleVersion slPluginMinRequired(0x51BE0000, 0x00010000); + + if (compareFileVersion(slPluginMinRequired) < 0) + return true; + } else if (fileName() == "npmozax.dll") { + // Bug 15217: Mozilla ActiveX control complains about missing xpcom_core.dll + return true; + } else if (name() == "Yahoo Application State Plugin") { + // https://bugs.webkit.org/show_bug.cgi?id=26860 + // Bug in Yahoo Application State plug-in earlier than 1.0.0.6 leads to heap corruption. + static const PlatformModuleVersion yahooAppStatePluginMinRequired(0x00000006, 0x00010000); + if (compareFileVersion(yahooAppStatePluginMinRequired) < 0) + return true; + } + + return false; +} + +void PluginPackage::determineQuirks(const String& mimeType) +{ + if (mimeType == "application/x-shockwave-flash") { + static const PlatformModuleVersion flashTenVersion(0x00000000, 0x000a0000); + + // Pre 10 Flash only requests windowless plugins if we return a mozilla user agent + if (compareFileVersion(flashTenVersion) < 0) + m_quirks.add(PluginQuirkWantsMozillaUserAgent); + + m_quirks.add(PluginQuirkThrottleInvalidate); + m_quirks.add(PluginQuirkThrottleWMUserPlusOneMessages); + m_quirks.add(PluginQuirkFlashURLNotifyBug); + } + + if (name().contains("Microsoft") && name().contains("Windows Media")) { + // The WMP plugin sets its size on the first NPP_SetWindow call and never updates its size, so + // call SetWindow when the plugin view has a correct size + m_quirks.add(PluginQuirkDeferFirstSetWindowCall); + + // Windowless mode does not work at all with the WMP plugin so just remove that parameter + // and don't pass it to the plug-in. + m_quirks.add(PluginQuirkRemoveWindowlessVideoParam); + + // WMP has a modal message loop that it enters whenever we call it or + // ask it to paint. This modal loop can deliver messages to other + // windows in WebKit at times when they are not expecting them (for + // example, delivering a WM_PAINT message during a layout), and these + // can cause crashes. + m_quirks.add(PluginQuirkHasModalMessageLoop); + } + + if (name() == "VLC Multimedia Plugin" || name() == "VLC Multimedia Plug-in") { + // VLC hangs on NPP_Destroy if we call NPP_SetWindow with a null window handle + m_quirks.add(PluginQuirkDontSetNullWindowHandleOnDestroy); + + // VLC 0.8.6d and 0.8.6e crash if multiple instances are created. + // <rdar://problem/5773070> tracks allowing multiple instances when this + // bug is fixed. + m_quirks.add(PluginQuirkDontAllowMultipleInstances); + } + + // The DivX plugin sets its size on the first NPP_SetWindow call and never updates its size, so + // call SetWindow when the plugin view has a correct size + if (mimeType == "video/divx") + m_quirks.add(PluginQuirkDeferFirstSetWindowCall); + + // FIXME: This is a workaround for a problem in our NPRuntime bindings; if a plug-in creates an + // NPObject and passes it to a function it's not possible to see what root object that NPObject belongs to. + // Thus, we don't know that the object should be invalidated when the plug-in instance goes away. + // See <rdar://problem/5487742>. + if (mimeType == "application/x-silverlight") + m_quirks.add(PluginQuirkDontUnloadPlugin); + + if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) { + // Because a single process cannot create multiple VMs, and we cannot reliably unload a + // Java VM, we cannot unload the Java plugin, or we'll lose reference to our only VM + m_quirks.add(PluginQuirkDontUnloadPlugin); + + // Setting the window region to an empty region causes bad scrolling repaint problems + // with the Java plug-in. + m_quirks.add(PluginQuirkDontClipToZeroRectWhenScrolling); + } + + if (mimeType == "audio/x-pn-realaudio-plugin") { + // Prevent the Real plugin from calling the Window Proc recursively, causing the stack to overflow. + m_quirks.add(PluginQuirkDontCallWndProcForSameMessageRecursively); + + static const PlatformModuleVersion lastKnownUnloadableRealPlayerVersion(0x000B0B24, 0x00060000); + + // Unloading RealPlayer versions newer than 10.5 can cause a hang; see rdar://5669317. + // FIXME: Resume unloading when this bug in the RealPlayer Plug-In is fixed (rdar://5713147) + if (compareFileVersion(lastKnownUnloadableRealPlayerVersion) > 0) + m_quirks.add(PluginQuirkDontUnloadPlugin); + } +} + +bool PluginPackage::fetchInfo() +{ + DWORD versionInfoSize, zeroHandle; + versionInfoSize = GetFileVersionInfoSizeW(const_cast<UChar*>(m_path.charactersWithNullTermination()), &zeroHandle); + if (versionInfoSize == 0) + return false; + + OwnArrayPtr<char> versionInfoData(new char[versionInfoSize]); + + if (!GetFileVersionInfoW(const_cast<UChar*>(m_path.charactersWithNullTermination()), + 0, versionInfoSize, versionInfoData.get())) + return false; + + m_name = getVersionInfo(versionInfoData.get(), "ProductName"); + m_description = getVersionInfo(versionInfoData.get(), "FileDescription"); + if (m_name.isNull() || m_description.isNull()) + return false; + + VS_FIXEDFILEINFO* info; + UINT infoSize; + if (!VerQueryValueW(versionInfoData.get(), L"\\", (LPVOID*) &info, &infoSize) || infoSize < sizeof(VS_FIXEDFILEINFO)) + return false; + m_moduleVersion.leastSig = info->dwFileVersionLS; + m_moduleVersion.mostSig = info->dwFileVersionMS; + + if (isPluginBlacklisted()) + return false; + + Vector<String> types; + getVersionInfo(versionInfoData.get(), "MIMEType").split('|', types); + Vector<String> extensionLists; + getVersionInfo(versionInfoData.get(), "FileExtents").split('|', extensionLists); + Vector<String> descriptions; + getVersionInfo(versionInfoData.get(), "FileOpenName").split('|', descriptions); + + for (unsigned i = 0; i < types.size(); i++) { + String type = types[i].lower(); + String description = i < descriptions.size() ? descriptions[i] : ""; + String extensionList = i < extensionLists.size() ? extensionLists[i] : ""; + + Vector<String> extensionsVector; + extensionList.split(',', extensionsVector); + + // Get rid of the extension list that may be at the end of the description string. + int pos = description.find("(*"); + if (pos != -1) { + // There might be a space that we need to get rid of. + if (pos > 1 && description[pos - 1] == ' ') + pos--; + description = description.left(pos); + } + + // Determine the quirks for the MIME types this plug-in supports + determineQuirks(type); + + m_mimeToExtensions.add(type, extensionsVector); + m_mimeToDescriptions.add(type, description); + } + + return true; +} + +bool PluginPackage::load() +{ + if (m_freeLibraryTimer.isActive()) { + ASSERT(m_module); + m_freeLibraryTimer.stop(); + } else if (m_isLoaded) { + if (m_quirks.contains(PluginQuirkDontAllowMultipleInstances)) + return false; + m_loadCount++; + return true; + } else { +#if OS(WINCE) + m_module = ::LoadLibraryW(m_path.charactersWithNullTermination()); +#else + WCHAR currentPath[MAX_PATH]; + + if (!::GetCurrentDirectoryW(MAX_PATH, currentPath)) + return false; + + String path = m_path.substring(0, m_path.reverseFind('\\')); + + if (!::SetCurrentDirectoryW(path.charactersWithNullTermination())) + return false; + + // Load the library + m_module = ::LoadLibraryExW(m_path.charactersWithNullTermination(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); + + if (!::SetCurrentDirectoryW(currentPath)) { + if (m_module) + ::FreeLibrary(m_module); + return false; + } +#endif + } + + if (!m_module) + return false; + + m_isLoaded = true; + + NP_GetEntryPointsFuncPtr NP_GetEntryPoints = 0; + NP_InitializeFuncPtr NP_Initialize = 0; + NPError npErr; + +#if OS(WINCE) + NP_Initialize = (NP_InitializeFuncPtr)GetProcAddress(m_module, L"NP_Initialize"); + NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)GetProcAddress(m_module, L"NP_GetEntryPoints"); + m_NPP_Shutdown = (NPP_ShutdownProcPtr)GetProcAddress(m_module, L"NP_Shutdown"); +#else + NP_Initialize = (NP_InitializeFuncPtr)GetProcAddress(m_module, "NP_Initialize"); + NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)GetProcAddress(m_module, "NP_GetEntryPoints"); + m_NPP_Shutdown = (NPP_ShutdownProcPtr)GetProcAddress(m_module, "NP_Shutdown"); +#endif + + if (!NP_Initialize || !NP_GetEntryPoints || !m_NPP_Shutdown) + goto abort; + + memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs)); + m_pluginFuncs.size = sizeof(m_pluginFuncs); + + npErr = NP_GetEntryPoints(&m_pluginFuncs); + LOG_NPERROR(npErr); + if (npErr != NPERR_NO_ERROR) + goto abort; + + initializeBrowserFuncs(); + + npErr = NP_Initialize(&m_browserFuncs); + LOG_NPERROR(npErr); + + if (npErr != NPERR_NO_ERROR) + goto abort; + + m_loadCount++; + return true; + +abort: + unloadWithoutShutdown(); + return false; +} + +unsigned PluginPackage::hash() const +{ + const unsigned hashCodes[] = { + m_name.impl()->hash(), + m_description.impl()->hash(), + m_mimeToExtensions.size() + }; + + return WTF::StringHasher::createBlobHash<sizeof(hashCodes)>(hashCodes); +} + +bool PluginPackage::equal(const PluginPackage& a, const PluginPackage& b) +{ + if (a.m_name != b.m_name) + return false; + + if (a.m_description != b.m_description) + return false; + + if (a.m_mimeToExtensions.size() != b.m_mimeToExtensions.size()) + return false; + + MIMEToExtensionsMap::const_iterator::Keys end = a.m_mimeToExtensions.end().keys(); + for (MIMEToExtensionsMap::const_iterator::Keys it = a.m_mimeToExtensions.begin().keys(); it != end; ++it) { + if (!b.m_mimeToExtensions.contains(*it)) + return false; + } + + return true; +} + +uint16_t PluginPackage::NPVersion() const +{ + return NP_VERSION_MINOR; +} +} diff --git a/Source/WebCore/plugins/win/PluginViewWin.cpp b/Source/WebCore/plugins/win/PluginViewWin.cpp new file mode 100644 index 0000000..758d90a --- /dev/null +++ b/Source/WebCore/plugins/win/PluginViewWin.cpp @@ -0,0 +1,1080 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. All rights reserved. + * Copyright (C) 2008-2009 Torch Mobile, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginView.h" + +#include "BitmapImage.h" +#include "Bridge.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Element.h" +#include "EventNames.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameLoadRequest.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLPlugInElement.h" +#include "HostWindow.h" +#include "Image.h" +#include "JSDOMBinding.h" +#include "JSDOMWindow.h" +#include "KeyboardEvent.h" +#include "LocalWindowsContext.h" +#include "MIMETypeRegistry.h" +#include "MouseEvent.h" +#include "Page.h" +#include "PlatformMouseEvent.h" +#include "PluginDatabase.h" +#include "PluginDebug.h" +#include "PluginMainThreadScheduler.h" +#include "PluginMessageThrottlerWin.h" +#include "PluginPackage.h" +#include "RenderWidget.h" +#include "ScriptController.h" +#include "Settings.h" +#include "WebCoreInstanceHandle.h" +#include "c_instance.h" +#include "npruntime_impl.h" +#include "runtime_root.h" +#include <runtime/JSLock.h> +#include <runtime/JSValue.h> +#include <wtf/ASCIICType.h> + +#if !PLATFORM(WX) +#include "BitmapInfo.h" +#endif + +#if OS(WINCE) +#undef LOG_NPERROR +#define LOG_NPERROR(x) +#undef LOG_PLUGIN_NET_ERROR +#define LOG_PLUGIN_NET_ERROR() +#endif + +#if PLATFORM(CAIRO) +#include <cairo-win32.h> +#endif + +#if PLATFORM(QT) +#include "QWebPageClient.h" +#include <QWidget> +#endif + +#if PLATFORM(WX) +#include <wx/defs.h> +#include <wx/window.h> +#endif + +static inline HWND windowHandleForPageClient(PlatformPageClient client) +{ +#if PLATFORM(QT) + if (!client) + return 0; + if (QWidget* pluginParent = qobject_cast<QWidget*>(client->pluginParent())) + return pluginParent->winId(); + return 0; +#elif PLATFORM(WX) + if (!client) + return 0; + return (HWND)client->GetHandle(); +#else + return client; +#endif +} + +using JSC::ExecState; +using JSC::JSLock; +using JSC::JSObject; +using JSC::UString; + +using std::min; + +using namespace WTF; + +namespace WebCore { + +using namespace HTMLNames; + +const LPCWSTR kWebPluginViewdowClassName = L"WebPluginView"; +const LPCWSTR kWebPluginViewProperty = L"WebPluginViewProperty"; + +#if !OS(WINCE) +// The code used to hook BeginPaint/EndPaint originally came from +// <http://www.fengyuan.com/article/wmprint.html>. +// Copyright (C) 2000 by Feng Yuan (www.fengyuan.com). + +static unsigned beginPaintSysCall; +static BYTE* beginPaint; + +static unsigned endPaintSysCall; +static BYTE* endPaint; + +typedef HDC (WINAPI *PtrBeginPaint)(HWND, PAINTSTRUCT*); +typedef BOOL (WINAPI *PtrEndPaint)(HWND, const PAINTSTRUCT*); + +#if OS(WINDOWS) && CPU(X86_64) && COMPILER(MSVC) +extern "C" HDC __stdcall _HBeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint); +extern "C" BOOL __stdcall _HEndPaint(HWND hWnd, const PAINTSTRUCT* lpPaint); +#endif + +HDC WINAPI PluginView::hookedBeginPaint(HWND hWnd, PAINTSTRUCT* lpPaint) +{ + PluginView* pluginView = reinterpret_cast<PluginView*>(GetProp(hWnd, kWebPluginViewProperty)); + if (pluginView && pluginView->m_wmPrintHDC) { + // We're secretly handling WM_PRINTCLIENT, so set up the PAINTSTRUCT so + // that the plugin will paint into the HDC we provide. + memset(lpPaint, 0, sizeof(PAINTSTRUCT)); + lpPaint->hdc = pluginView->m_wmPrintHDC; + GetClientRect(hWnd, &lpPaint->rcPaint); + return pluginView->m_wmPrintHDC; + } + +#if COMPILER(GCC) + HDC result; + asm ("push %2\n" + "push %3\n" + "call *%4\n" + : "=a" (result) + : "a" (beginPaintSysCall), "g" (lpPaint), "g" (hWnd), "m" (beginPaint) + : "memory" + ); + return result; +#elif defined(_M_IX86) + // Call through to the original BeginPaint. + __asm mov eax, beginPaintSysCall + __asm push lpPaint + __asm push hWnd + __asm call beginPaint +#else + return _HBeginPaint(hWnd, lpPaint); +#endif +} + +BOOL WINAPI PluginView::hookedEndPaint(HWND hWnd, const PAINTSTRUCT* lpPaint) +{ + PluginView* pluginView = reinterpret_cast<PluginView*>(GetProp(hWnd, kWebPluginViewProperty)); + if (pluginView && pluginView->m_wmPrintHDC) { + // We're secretly handling WM_PRINTCLIENT, so we don't have to do any + // cleanup. + return TRUE; + } + +#if COMPILER(GCC) + BOOL result; + asm ("push %2\n" + "push %3\n" + "call *%4\n" + : "=a" (result) + : "a" (endPaintSysCall), "g" (lpPaint), "g" (hWnd), "m" (endPaint) + ); + return result; +#elif defined (_M_IX86) + // Call through to the original EndPaint. + __asm mov eax, endPaintSysCall + __asm push lpPaint + __asm push hWnd + __asm call endPaint +#else + return _HEndPaint(hWnd, lpPaint); +#endif +} + +static void hook(const char* module, const char* proc, unsigned& sysCallID, BYTE*& pProc, const void* pNewProc) +{ + // See <http://www.fengyuan.com/article/wmprint.html> for an explanation of + // how this function works. + + HINSTANCE hMod = GetModuleHandleA(module); + + pProc = reinterpret_cast<BYTE*>(reinterpret_cast<ptrdiff_t>(GetProcAddress(hMod, proc))); + +#if COMPILER(GCC) || defined(_M_IX86) + if (pProc[0] != 0xB8) + return; + + // FIXME: Should we be reading the bytes one-by-one instead of doing an + // unaligned read? + sysCallID = *reinterpret_cast<unsigned*>(pProc + 1); + + DWORD flOldProtect; + if (!VirtualProtect(pProc, 5, PAGE_EXECUTE_READWRITE, &flOldProtect)) + return; + + pProc[0] = 0xE9; + *reinterpret_cast<unsigned*>(pProc + 1) = reinterpret_cast<intptr_t>(pNewProc) - reinterpret_cast<intptr_t>(pProc + 5); + + pProc += 5; +#else + /* Disassembly of BeginPaint() + 00000000779FC5B0 4C 8B D1 mov r10,rcx + 00000000779FC5B3 B8 17 10 00 00 mov eax,1017h + 00000000779FC5B8 0F 05 syscall + 00000000779FC5BA C3 ret + 00000000779FC5BB 90 nop + 00000000779FC5BC 90 nop + 00000000779FC5BD 90 nop + 00000000779FC5BE 90 nop + 00000000779FC5BF 90 nop + 00000000779FC5C0 90 nop + 00000000779FC5C1 90 nop + 00000000779FC5C2 90 nop + 00000000779FC5C3 90 nop + */ + // Check for the signature as in the above disassembly + DWORD guard = 0xB8D18B4C; + if (*reinterpret_cast<DWORD*>(pProc) != guard) + return; + + DWORD flOldProtect; + VirtualProtect(pProc, 12, PAGE_EXECUTE_READWRITE, & flOldProtect); + pProc[0] = 0x48; // mov rax, this + pProc[1] = 0xb8; + *(__int64*)(pProc+2) = (__int64)pNewProc; + pProc[10] = 0xff; // jmp rax + pProc[11] = 0xe0; +#endif +} + +static void setUpOffscreenPaintingHooks(HDC (WINAPI*hookedBeginPaint)(HWND, PAINTSTRUCT*), BOOL (WINAPI*hookedEndPaint)(HWND, const PAINTSTRUCT*)) +{ + static bool haveHooked = false; + if (haveHooked) + return; + haveHooked = true; + + // Most (all?) windowed plugins don't seem to respond to WM_PRINTCLIENT, so + // we hook into BeginPaint/EndPaint to allow their normal WM_PAINT handling + // to draw into a given HDC. Note that this hooking affects the entire + // process. + hook("user32.dll", "BeginPaint", beginPaintSysCall, beginPaint, reinterpret_cast<const void *>(reinterpret_cast<ptrdiff_t>(hookedBeginPaint))); + hook("user32.dll", "EndPaint", endPaintSysCall, endPaint, reinterpret_cast<const void *>(reinterpret_cast<ptrdiff_t>(hookedEndPaint))); + +} +#endif + +static bool registerPluginView() +{ + static bool haveRegisteredWindowClass = false; + if (haveRegisteredWindowClass) + return true; + + haveRegisteredWindowClass = true; + +#if PLATFORM(QT) + WebCore::setInstanceHandle((HINSTANCE)(qWinAppInst())); +#endif + + ASSERT(WebCore::instanceHandle()); + +#if OS(WINCE) + WNDCLASS wcex = { 0 }; +#else + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.hIconSm = 0; +#endif + + wcex.style = CS_DBLCLKS; +#if OS(WINCE) + wcex.style |= CS_PARENTDC; +#endif + wcex.lpfnWndProc = DefWindowProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = WebCore::instanceHandle(); + wcex.hIcon = 0; + wcex.hCursor = LoadCursor(0, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)COLOR_WINDOW; + wcex.lpszMenuName = 0; + wcex.lpszClassName = kWebPluginViewdowClassName; + +#if OS(WINCE) + return !!RegisterClass(&wcex); +#else + return !!RegisterClassEx(&wcex); +#endif +} + +LRESULT CALLBACK PluginView::PluginViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PluginView* pluginView = reinterpret_cast<PluginView*>(GetProp(hWnd, kWebPluginViewProperty)); + + return pluginView->wndProc(hWnd, message, wParam, lParam); +} + +static bool isWindowsMessageUserGesture(UINT message) +{ + switch (message) { + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_KEYUP: + return true; + default: + return false; + } +} + +LRESULT +PluginView::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + // <rdar://5711136> Sometimes Flash will call SetCapture before creating + // a full-screen window and will not release it, which causes the + // full-screen window to never receive mouse events. We set/release capture + // on mouse down/up before sending the event to the plug-in to prevent that. + switch (message) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + ::SetCapture(hWnd); + break; + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + ::ReleaseCapture(); + break; + } + + if (message == m_lastMessage && + m_plugin->quirks().contains(PluginQuirkDontCallWndProcForSameMessageRecursively) && + m_isCallingPluginWndProc) + return 1; + + if (message == WM_USER + 1 && + m_plugin->quirks().contains(PluginQuirkThrottleWMUserPlusOneMessages)) { + if (!m_messageThrottler) + m_messageThrottler.set(new PluginMessageThrottlerWin(this)); + + m_messageThrottler->appendMessage(hWnd, message, wParam, lParam); + return 0; + } + + m_lastMessage = message; + m_isCallingPluginWndProc = true; + + // If the plug-in doesn't explicitly support changing the pop-up state, we enable + // popups for all user gestures. + // Note that we need to pop the state in a timer, because the Flash plug-in + // pops up windows in response to a posted message. + if (m_plugin->pluginFuncs()->version < NPVERS_HAS_POPUPS_ENABLED_STATE && + isWindowsMessageUserGesture(message) && !m_popPopupsStateTimer.isActive()) { + + pushPopupsEnabledState(true); + + m_popPopupsStateTimer.startOneShot(0); + } + +#if !OS(WINCE) + if (message == WM_PRINTCLIENT) { + // Most (all?) windowed plugins don't respond to WM_PRINTCLIENT, so we + // change the message to WM_PAINT and rely on our hooked versions of + // BeginPaint/EndPaint to make the plugin draw into the given HDC. + message = WM_PAINT; + m_wmPrintHDC = reinterpret_cast<HDC>(wParam); + } +#endif + + // Call the plug-in's window proc. + LRESULT result = ::CallWindowProc(m_pluginWndProc, hWnd, message, wParam, lParam); + + m_wmPrintHDC = 0; + + m_isCallingPluginWndProc = false; + + return result; +} + +void PluginView::updatePluginWidget() +{ + if (!parent()) + return; + + ASSERT(parent()->isFrameView()); + FrameView* frameView = static_cast<FrameView*>(parent()); + + IntRect oldWindowRect = m_windowRect; + IntRect oldClipRect = m_clipRect; + +#if OS(WINCE) + m_windowRect = frameView->contentsToWindow(frameRect()); +#else + m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size()); +#endif + m_clipRect = windowClipRect(); + m_clipRect.move(-m_windowRect.x(), -m_windowRect.y()); + + if (platformPluginWidget() && (!m_haveUpdatedPluginWidget || m_windowRect != oldWindowRect || m_clipRect != oldClipRect)) { + HRGN rgn; + + setCallingPlugin(true); + + // To prevent flashes while scrolling, we disable drawing during the window + // update process by clipping the window to the zero rect. + + bool clipToZeroRect = !m_plugin->quirks().contains(PluginQuirkDontClipToZeroRectWhenScrolling); + + if (clipToZeroRect) { + rgn = ::CreateRectRgn(0, 0, 0, 0); + ::SetWindowRgn(platformPluginWidget(), rgn, FALSE); + } else { + rgn = ::CreateRectRgn(m_clipRect.x(), m_clipRect.y(), m_clipRect.right(), m_clipRect.bottom()); + ::SetWindowRgn(platformPluginWidget(), rgn, TRUE); + } + + if (!m_haveUpdatedPluginWidget || m_windowRect != oldWindowRect) + ::MoveWindow(platformPluginWidget(), m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), TRUE); + + if (clipToZeroRect) { + rgn = ::CreateRectRgn(m_clipRect.x(), m_clipRect.y(), m_clipRect.right(), m_clipRect.bottom()); + ::SetWindowRgn(platformPluginWidget(), rgn, TRUE); + } + + setCallingPlugin(false); + + m_haveUpdatedPluginWidget = true; + } +} + +void PluginView::setFocus(bool focused) +{ + if (focused && platformPluginWidget()) + SetFocus(platformPluginWidget()); + + Widget::setFocus(focused); +} + +void PluginView::show() +{ + setSelfVisible(true); + + if (isParentVisible() && platformPluginWidget()) + ShowWindow(platformPluginWidget(), SW_SHOWNA); + + Widget::show(); +} + +void PluginView::hide() +{ + setSelfVisible(false); + + if (isParentVisible() && platformPluginWidget()) + ShowWindow(platformPluginWidget(), SW_HIDE); + + Widget::hide(); +} + +bool PluginView::dispatchNPEvent(NPEvent& npEvent) +{ + if (!m_plugin->pluginFuncs()->event) + return true; + + bool shouldPop = false; + + if (m_plugin->pluginFuncs()->version < NPVERS_HAS_POPUPS_ENABLED_STATE && isWindowsMessageUserGesture(npEvent.event)) { + pushPopupsEnabledState(true); + shouldPop = true; + } + + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + setCallingPlugin(true); + bool result = m_plugin->pluginFuncs()->event(m_instance, &npEvent); + setCallingPlugin(false); + + if (shouldPop) + popPopupsEnabledState(); + + return result; +} + +void PluginView::paintIntoTransformedContext(HDC hdc) +{ + if (m_isWindowed) { +#if !OS(WINCE) + SendMessage(platformPluginWidget(), WM_PRINTCLIENT, reinterpret_cast<WPARAM>(hdc), PRF_CLIENT | PRF_CHILDREN | PRF_OWNED); +#endif + return; + } + + m_npWindow.type = NPWindowTypeDrawable; + m_npWindow.window = hdc; + + WINDOWPOS windowpos = { 0, 0, 0, 0, 0, 0, 0 }; + + IntRect r = static_cast<FrameView*>(parent())->contentsToWindow(frameRect()); + + windowpos.x = r.x(); + windowpos.y = r.y(); + windowpos.cx = r.width(); + windowpos.cy = r.height(); + + NPEvent npEvent; + npEvent.event = WM_WINDOWPOSCHANGED; + npEvent.lParam = reinterpret_cast<uintptr_t>(&windowpos); + npEvent.wParam = 0; + + dispatchNPEvent(npEvent); + + setNPWindowRect(frameRect()); + + npEvent.event = WM_PAINT; + npEvent.wParam = reinterpret_cast<uintptr_t>(hdc); + + // This is supposed to be a pointer to the dirty rect, but it seems that the Flash plugin + // ignores it so we just pass null. + npEvent.lParam = 0; + + dispatchNPEvent(npEvent); +} + +void PluginView::paintWindowedPluginIntoContext(GraphicsContext* context, const IntRect& rect) +{ +#if !OS(WINCE) + ASSERT(m_isWindowed); + ASSERT(context->shouldIncludeChildWindows()); + + ASSERT(parent()->isFrameView()); + IntPoint locationInWindow = static_cast<FrameView*>(parent())->contentsToWindow(frameRect().location()); + + LocalWindowsContext windowsContext(context, frameRect(), false); + +#if PLATFORM(CAIRO) + // Must flush drawings up to this point to the backing metafile, otherwise the + // plugin region will be overwritten with any clear regions specified in the + // cairo-controlled portions of the rendering. + PlatformGraphicsContext* ctx = context->platformContext(); + cairo_show_page(ctx); +#endif + + HDC hdc = windowsContext.hdc(); + XFORM originalTransform; + GetWorldTransform(hdc, &originalTransform); + + // The plugin expects the DC to be in client coordinates, so we translate + // the DC to make that so. + AffineTransform ctm = context->getCTM(); + ctm.translate(locationInWindow.x(), locationInWindow.y()); + XFORM transform = static_cast<XFORM>(ctm.toTransformationMatrix()); + + SetWorldTransform(hdc, &transform); + + paintIntoTransformedContext(hdc); + + SetWorldTransform(hdc, &originalTransform); +#endif +} + +void PluginView::paint(GraphicsContext* context, const IntRect& rect) +{ + if (!m_isStarted) { + // Draw the "missing plugin" image + paintMissingPluginIcon(context, rect); + return; + } + + if (context->paintingDisabled()) + return; + + // Ensure that we have called SetWindow before we try to paint. + if (!m_haveCalledSetWindow) + setNPWindowRect(frameRect()); + + if (m_isWindowed) { +#if !OS(WINCE) + if (context->shouldIncludeChildWindows()) + paintWindowedPluginIntoContext(context, rect); +#endif + return; + } + + ASSERT(parent()->isFrameView()); + IntRect rectInWindow = static_cast<FrameView*>(parent())->contentsToWindow(frameRect()); + LocalWindowsContext windowsContext(context, rectInWindow, m_isTransparent); + + // On Safari/Windows without transparency layers the GraphicsContext returns the HDC + // of the window and the plugin expects that the passed in DC has window coordinates. + // In the Qt port we always draw in an offscreen buffer and therefore need to preserve + // the translation set in getWindowsContext. +#if !PLATFORM(QT) && !OS(WINCE) + if (!context->inTransparencyLayer()) { + XFORM transform; + GetWorldTransform(windowsContext.hdc(), &transform); + transform.eDx = 0; + transform.eDy = 0; + SetWorldTransform(windowsContext.hdc(), &transform); + } +#endif + + paintIntoTransformedContext(windowsContext.hdc()); +} + +void PluginView::handleKeyboardEvent(KeyboardEvent* event) +{ + NPEvent npEvent; + + npEvent.wParam = event->keyCode(); + + if (event->type() == eventNames().keydownEvent) { + npEvent.event = WM_KEYDOWN; + npEvent.lParam = 0; + } else if (event->type() == eventNames().keyupEvent) { + npEvent.event = WM_KEYUP; + npEvent.lParam = 0x8000; + } + + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + if (!dispatchNPEvent(npEvent)) + event->setDefaultHandled(); +} + +#if !OS(WINCE) +extern bool ignoreNextSetCursor; +#endif + +void PluginView::handleMouseEvent(MouseEvent* event) +{ + NPEvent npEvent; + + IntPoint p = static_cast<FrameView*>(parent())->contentsToWindow(IntPoint(event->pageX(), event->pageY())); + + npEvent.lParam = MAKELPARAM(p.x(), p.y()); + npEvent.wParam = 0; + + if (event->ctrlKey()) + npEvent.wParam |= MK_CONTROL; + if (event->shiftKey()) + npEvent.wParam |= MK_SHIFT; + + if (event->type() == eventNames().mousemoveEvent || + event->type() == eventNames().mouseoutEvent || + event->type() == eventNames().mouseoverEvent) { + npEvent.event = WM_MOUSEMOVE; + if (event->buttonDown()) + switch (event->button()) { + case LeftButton: + npEvent.wParam |= MK_LBUTTON; + break; + case MiddleButton: + npEvent.wParam |= MK_MBUTTON; + break; + case RightButton: + npEvent.wParam |= MK_RBUTTON; + break; + } + } + else if (event->type() == eventNames().mousedownEvent) { + focusPluginElement(); + switch (event->button()) { + case 0: + npEvent.event = WM_LBUTTONDOWN; + break; + case 1: + npEvent.event = WM_MBUTTONDOWN; + break; + case 2: + npEvent.event = WM_RBUTTONDOWN; + break; + } + } else if (event->type() == eventNames().mouseupEvent) { + switch (event->button()) { + case 0: + npEvent.event = WM_LBUTTONUP; + break; + case 1: + npEvent.event = WM_MBUTTONUP; + break; + case 2: + npEvent.event = WM_RBUTTONUP; + break; + } + } else + return; + + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + if (!dispatchNPEvent(npEvent)) + event->setDefaultHandled(); + +#if !PLATFORM(QT) && !PLATFORM(WX) && !OS(WINCE) + // Currently, Widget::setCursor is always called after this function in EventHandler.cpp + // and since we don't want that we set ignoreNextSetCursor to true here to prevent that. + ignoreNextSetCursor = true; + if (Page* page = m_parentFrame->page()) + page->chrome()->client()->setLastSetCursorToCurrentCursor(); +#endif +} + +void PluginView::setParent(ScrollView* parent) +{ + Widget::setParent(parent); + +#if OS(WINCE) + if (parent) { + init(); + if (parent->isVisible()) + show(); + else + hide(); + } +#else + if (parent) + init(); + else { + if (!platformPluginWidget()) + return; + + // If the plug-in window or one of its children have the focus, we need to + // clear it to prevent the web view window from being focused because that can + // trigger a layout while the plugin element is being detached. + HWND focusedWindow = ::GetFocus(); + if (platformPluginWidget() == focusedWindow || ::IsChild(platformPluginWidget(), focusedWindow)) + ::SetFocus(0); + } +#endif +} + +void PluginView::setParentVisible(bool visible) +{ + if (isParentVisible() == visible) + return; + + Widget::setParentVisible(visible); + + if (isSelfVisible() && platformPluginWidget()) { + if (visible) + ShowWindow(platformPluginWidget(), SW_SHOWNA); + else + ShowWindow(platformPluginWidget(), SW_HIDE); + } +} + +void PluginView::setNPWindowRect(const IntRect& rect) +{ + if (!m_isStarted) + return; + +#if OS(WINCE) + IntRect r = static_cast<FrameView*>(parent())->contentsToWindow(rect); + m_npWindow.x = r.x(); + m_npWindow.y = r.y(); + + m_npWindow.width = r.width(); + m_npWindow.height = r.height(); + + m_npWindow.clipRect.right = r.width(); + m_npWindow.clipRect.bottom = r.height(); +#else + IntPoint p = static_cast<FrameView*>(parent())->contentsToWindow(rect.location()); + m_npWindow.x = p.x(); + m_npWindow.y = p.y(); + + m_npWindow.width = rect.width(); + m_npWindow.height = rect.height(); + + m_npWindow.clipRect.right = rect.width(); + m_npWindow.clipRect.bottom = rect.height(); +#endif + m_npWindow.clipRect.left = 0; + m_npWindow.clipRect.top = 0; + + if (m_plugin->pluginFuncs()->setwindow) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + setCallingPlugin(true); + m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); + setCallingPlugin(false); + + m_haveCalledSetWindow = true; + + if (!m_isWindowed) + return; + + ASSERT(platformPluginWidget()); + +#if OS(WINCE) + if (!m_pluginWndProc) { + WNDPROC currentWndProc = (WNDPROC)GetWindowLong(platformPluginWidget(), GWL_WNDPROC); + if (currentWndProc != PluginViewWndProc) + m_pluginWndProc = (WNDPROC)SetWindowLong(platformPluginWidget(), GWL_WNDPROC, (LONG)PluginViewWndProc); + } +#else + WNDPROC currentWndProc = (WNDPROC)GetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC); + if (currentWndProc != PluginViewWndProc) + m_pluginWndProc = (WNDPROC)SetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC, (LONG_PTR)PluginViewWndProc); +#endif + } +} + +NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf) +{ + String filename(buf, len); + + if (filename.startsWith("file:///")) + filename = filename.substring(8); + + // Get file info + WIN32_FILE_ATTRIBUTE_DATA attrs; + if (GetFileAttributesExW(filename.charactersWithNullTermination(), GetFileExInfoStandard, &attrs) == 0) + return NPERR_FILE_NOT_FOUND; + + if (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + return NPERR_FILE_NOT_FOUND; + + HANDLE fileHandle = CreateFileW(filename.charactersWithNullTermination(), FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + + if (fileHandle == INVALID_HANDLE_VALUE) + return NPERR_FILE_NOT_FOUND; + + buffer.resize(attrs.nFileSizeLow); + + DWORD bytesRead; + int retval = ReadFile(fileHandle, buffer.data(), attrs.nFileSizeLow, &bytesRead, 0); + + CloseHandle(fileHandle); + + if (retval == 0 || bytesRead != attrs.nFileSizeLow) + return NPERR_FILE_NOT_FOUND; + + return NPERR_NO_ERROR; +} + +bool PluginView::platformGetValueStatic(NPNVariable, void*, NPError*) +{ + return false; +} + +bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result) +{ + switch (variable) { + case NPNVnetscapeWindow: { + HWND* w = reinterpret_cast<HWND*>(value); + *w = windowHandleForPageClient(parent() ? parent()->hostWindow()->platformPageClient() : 0); + *result = NPERR_NO_ERROR; + return true; + } + + case NPNVSupportsWindowless: { + NPBool* flag = reinterpret_cast<NPBool*>(value); + *flag = TRUE; + *result = NPERR_NO_ERROR; + return true; + } + + default: + return false; + } +} + +void PluginView::invalidateRect(const IntRect& rect) +{ + if (m_isWindowed) { + RECT invalidRect = { rect.x(), rect.y(), rect.right(), rect.bottom() }; + ::InvalidateRect(platformPluginWidget(), &invalidRect, false); + return; + } + + invalidateWindowlessPluginRect(rect); +} + +void PluginView::invalidateRect(NPRect* rect) +{ + if (!rect) { + invalidate(); + return; + } + + IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top); + + if (m_isWindowed) { + RECT invalidRect = { r.x(), r.y(), r.right(), r.bottom() }; + InvalidateRect(platformPluginWidget(), &invalidRect, FALSE); + } else { + if (m_plugin->quirks().contains(PluginQuirkThrottleInvalidate)) { + m_invalidRects.append(r); + if (!m_invalidateTimer.isActive()) + m_invalidateTimer.startOneShot(0.001); + } else + invalidateRect(r); + } +} + +void PluginView::invalidateRegion(NPRegion region) +{ + if (m_isWindowed) + return; + + RECT r; + + if (GetRgnBox(region, &r) == 0) { + invalidate(); + return; + } + + IntRect rect(IntPoint(r.left, r.top), IntSize(r.right-r.left, r.bottom-r.top)); + invalidateRect(rect); +} + +void PluginView::forceRedraw() +{ + if (m_isWindowed) + ::UpdateWindow(platformPluginWidget()); + else + ::UpdateWindow(windowHandleForPageClient(parent() ? parent()->hostWindow()->platformPageClient() : 0)); +} + +bool PluginView::platformStart() +{ + ASSERT(m_isStarted); + ASSERT(m_status == PluginStatusLoadedSuccessfully); + + if (m_isWindowed) { + registerPluginView(); +#if !OS(WINCE) + setUpOffscreenPaintingHooks(hookedBeginPaint, hookedEndPaint); +#endif + + DWORD flags = WS_CHILD; + if (isSelfVisible()) + flags |= WS_VISIBLE; + + HWND parentWindowHandle = windowHandleForPageClient(m_parentFrame->view()->hostWindow()->platformPageClient()); + HWND window = ::CreateWindowEx(0, kWebPluginViewdowClassName, 0, flags, + 0, 0, 0, 0, parentWindowHandle, 0, WebCore::instanceHandle(), 0); + +#if OS(WINDOWS) && (PLATFORM(QT) || PLATFORM(WX)) + m_window = window; +#else + setPlatformWidget(window); +#endif + + // Calling SetWindowLongPtrA here makes the window proc ASCII, which is required by at least + // the Shockwave Director plug-in. +#if OS(WINDOWS) && CPU(X86_64) + ::SetWindowLongPtrA(platformPluginWidget(), GWLP_WNDPROC, (LONG_PTR)DefWindowProcA); +#elif OS(WINCE) + ::SetWindowLong(platformPluginWidget(), GWL_WNDPROC, (LONG)DefWindowProc); +#else + ::SetWindowLongPtrA(platformPluginWidget(), GWL_WNDPROC, (LONG)DefWindowProcA); +#endif + SetProp(platformPluginWidget(), kWebPluginViewProperty, this); + + m_npWindow.type = NPWindowTypeWindow; + m_npWindow.window = platformPluginWidget(); + } else { + m_npWindow.type = NPWindowTypeDrawable; + m_npWindow.window = 0; + } + + updatePluginWidget(); + + if (!m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall)) + setNPWindowRect(frameRect()); + + return true; +} + +void PluginView::platformDestroy() +{ + if (!platformPluginWidget()) + return; + + DestroyWindow(platformPluginWidget()); + setPlatformPluginWidget(0); +} + +PassRefPtr<Image> PluginView::snapshot() +{ +#if !PLATFORM(WX) && !OS(WINCE) + OwnPtr<HDC> hdc(CreateCompatibleDC(0)); + + if (!m_isWindowed) { + // Enable world transforms. + SetGraphicsMode(hdc.get(), GM_ADVANCED); + + XFORM transform; + GetWorldTransform(hdc.get(), &transform); + + // Windowless plug-ins assume that they're drawing onto the view's DC. + // Translate the context so that the plug-in draws at (0, 0). + ASSERT(parent()->isFrameView()); + IntPoint position = static_cast<FrameView*>(parent())->contentsToWindow(frameRect()).location(); + transform.eDx = -position.x(); + transform.eDy = -position.y(); + SetWorldTransform(hdc.get(), &transform); + } + + void* bits; + BitmapInfo bmp = BitmapInfo::createBottomUp(frameRect().size()); + OwnPtr<HBITMAP> hbmp(CreateDIBSection(0, &bmp, DIB_RGB_COLORS, &bits, 0, 0)); + + HBITMAP hbmpOld = static_cast<HBITMAP>(SelectObject(hdc.get(), hbmp.get())); + + paintIntoTransformedContext(hdc.get()); + + SelectObject(hdc.get(), hbmpOld); + + return BitmapImage::create(hbmp.get()); +#else + return 0; +#endif +} + +void PluginView::halt() +{ + ASSERT(!m_isHalted); + ASSERT(m_isStarted); + +#if !PLATFORM(QT) + // Show a screenshot of the plug-in. + toRenderWidget(m_element->renderer())->showSubstituteImage(snapshot()); +#endif + + m_isHalted = true; + m_hasBeenHalted = true; + + stop(); + platformDestroy(); +} + +void PluginView::restart() +{ + ASSERT(!m_isStarted); + ASSERT(m_isHalted); + + // Clear any substitute image. + toRenderWidget(m_element->renderer())->showSubstituteImage(0); + + m_isHalted = false; + m_haveUpdatedPluginWidget = false; + start(); +} + +} // namespace WebCore |