// Copyright (c) 2006-2009 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 "config.h" #include "webkit/glue/webkit_glue.h" #if defined(OS_WIN) #include #include #elif defined(OS_POSIX) && !defined(OS_MACOSX) #include #endif #include "BackForwardList.h" #include "Document.h" #include "FrameTree.h" #include "FrameView.h" #include "Frame.h" #include "GlyphPageTreeNode.h" #include "HistoryItem.h" #include "ImageSource.h" #include "KURL.h" #include "Page.h" #include "PlatformString.h" #include "RenderTreeAsText.h" #include "RenderView.h" #include "ScriptController.h" #include "SharedBuffer.h" #undef LOG #include "base/file_version_info.h" #include "base/singleton.h" #include "base/string_piece.h" #include "base/string_util.h" #include "base/sys_info.h" #include "base/sys_string_conversions.h" #include "net/base/escape.h" #include "skia/ext/platform_canvas.h" #if defined(OS_MACOSX) #include "skia/ext/skia_utils_mac.h" #endif #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/WebKit/WebKit/chromium/public/WebData.h" #include "third_party/WebKit/WebKit/chromium/public/WebHistoryItem.h" #include "third_party/WebKit/WebKit/chromium/public/WebImage.h" #include "third_party/WebKit/WebKit/chromium/public/WebSize.h" #include "third_party/WebKit/WebKit/chromium/public/WebString.h" #include "third_party/WebKit/WebKit/chromium/public/WebVector.h" #if defined(OS_WIN) #include "third_party/WebKit/WebKit/chromium/public/win/WebInputEventFactory.h" #endif #include "third_party/WebKit/WebKit/chromium/src/WebFrameImpl.h" #include "third_party/WebKit/WebKit/chromium/src/WebViewImpl.h" #include "webkit/glue/glue_serialize.h" #include "webkit/glue/glue_util.h" #include "webkit_version.h" // Generated using WebKit::WebCanvas; using WebKit::WebFrame; using WebKit::WebFrameImpl; using WebKit::WebHistoryItem; using WebKit::WebString; using WebKit::WebVector; using WebKit::WebView; using WebKit::WebViewImpl; namespace { static const char kLayoutTestsPattern[] = "/LayoutTests/"; static const std::string::size_type kLayoutTestsPatternSize = arraysize(kLayoutTestsPattern) - 1; static const char kFileUrlPattern[] = "file:/"; static const char kDataUrlPattern[] = "data:"; static const std::string::size_type kDataUrlPatternSize = arraysize(kDataUrlPattern) - 1; static const char kFileTestPrefix[] = "(file test):"; static const char kChrome1ProductString[] = "Chrome/1.0.154.53"; } //------------------------------------------------------------------------------ // webkit_glue impl: namespace webkit_glue { // Global variable used by the plugin quirk "die after unload". bool g_forcefully_terminate_plugin_process = false; void SetJavaScriptFlags(const std::wstring& str) { #if USE(V8) std::string utf8_str = WideToUTF8(str); WebCore::ScriptController::setFlags(utf8_str.data(), static_cast(utf8_str.size())); #endif } void EnableWebCoreNotImplementedLogging() { WebCore::LogNotYetImplemented.state = WTFLogChannelOn; } std::wstring DumpDocumentText(WebFrame* web_frame) { WebFrameImpl* webFrameImpl = static_cast(web_frame); WebCore::Frame* frame = webFrameImpl->frame(); // We use the document element's text instead of the body text here because // not all documents have a body, such as XML documents. WebCore::Element* documentElement = frame->document()->documentElement(); if (!documentElement) { return std::wstring(); } return StringToStdWString(documentElement->innerText()); } std::wstring DumpFramesAsText(WebFrame* web_frame, bool recursive) { WebFrameImpl* webFrameImpl = static_cast(web_frame); std::wstring result; // Add header for all but the main frame. Skip empty frames. if (webFrameImpl->parent() && webFrameImpl->frame()->document()->documentElement()) { result.append(L"\n--------\nFrame: '"); result.append(UTF16ToWideHack(webFrameImpl->name())); result.append(L"'\n--------\n"); } result.append(DumpDocumentText(web_frame)); result.append(L"\n"); if (recursive) { WebCore::Frame* child = webFrameImpl->frame()->tree()->firstChild(); for (; child; child = child->tree()->nextSibling()) { result.append( DumpFramesAsText(WebFrameImpl::fromFrame(child), recursive)); } } return result; } std::wstring DumpRenderer(WebFrame* web_frame) { WebFrameImpl* webFrameImpl = static_cast(web_frame); WebCore::Frame* frame = webFrameImpl->frame(); WebCore::String frameText = WebCore::externalRepresentation(frame); return StringToStdWString(frameText); } bool CounterValueForElementById(WebFrame* web_frame, const std::string& id, std::wstring* counter_value) { WebFrameImpl* webFrameImpl = static_cast(web_frame); WebCore::Frame* frame = webFrameImpl->frame(); WebCore::Element* element = frame->document()->getElementById(WebCore::AtomicString(id.c_str())); if (!element) return false; WebCore::String counterValue = WebCore::counterValueForElement(element); *counter_value = StringToStdWString(counterValue); return true; } std::wstring DumpFrameScrollPosition(WebFrame* web_frame, bool recursive) { WebFrameImpl* webFrameImpl = static_cast(web_frame); WebCore::IntSize offset = webFrameImpl->frameView()->scrollOffset(); std::wstring result; if (offset.width() > 0 || offset.height() > 0) { if (webFrameImpl->parent()) { StringAppendF(&result, L"frame '%ls' ", StringToStdWString( webFrameImpl->frame()->tree()->name()).c_str()); } StringAppendF(&result, L"scrolled to %d,%d\n", offset.width(), offset.height()); } if (recursive) { WebCore::Frame* child = webFrameImpl->frame()->tree()->firstChild(); for (; child; child = child->tree()->nextSibling()) { result.append(DumpFrameScrollPosition(WebFrameImpl::fromFrame(child), recursive)); } } return result; } // Returns True if item1 < item2. static bool HistoryItemCompareLess(const WebHistoryItem& item1, const WebHistoryItem& item2) { string16 target1 = item1.target(); string16 target2 = item2.target(); std::transform(target1.begin(), target1.end(), target1.begin(), tolower); std::transform(target2.begin(), target2.end(), target2.begin(), tolower); return target1 < target2; } // Writes out a HistoryItem into a string in a readable format. static std::wstring DumpHistoryItem(const WebHistoryItem& item, int indent, bool is_current) { std::wstring result; if (is_current) { result.append(L"curr->"); result.append(indent - 6, L' '); // 6 == L"curr->".length() } else { result.append(indent, L' '); } std::string url = item.urlString().utf8(); size_t pos; if (url.find(kFileUrlPattern) == 0 && ((pos = url.find(kLayoutTestsPattern)) != std::string::npos)) { // adjust file URLs to match upstream results. url.replace(0, pos + kLayoutTestsPatternSize, kFileTestPrefix); } else if (url.find(kDataUrlPattern) == 0) { // URL-escape data URLs to match results upstream. std::string path = EscapePath(url.substr(kDataUrlPatternSize)); url.replace(kDataUrlPatternSize, url.length(), path); } result.append(UTF8ToWide(url)); if (!item.target().isEmpty()) result.append(L" (in frame \"" + UTF16ToWide(item.target()) + L"\")"); if (item.isTargetItem()) result.append(L" **nav target**"); result.append(L"\n"); const WebVector& children = item.children(); if (!children.isEmpty()) { // Must sort to eliminate arbitrary result ordering which defeats // reproducible testing. // TODO(darin): WebVector should probably just be a std::vector!! std::vector sorted_children; for (size_t i = 0; i < children.size(); ++i) sorted_children.push_back(children[i]); std::sort(sorted_children.begin(), sorted_children.end(), HistoryItemCompareLess); for (size_t i = 0; i < sorted_children.size(); i++) result += DumpHistoryItem(sorted_children[i], indent+4, false); } return result; } std::wstring DumpHistoryState(const std::string& history_state, int indent, bool is_current) { return DumpHistoryItem(HistoryItemFromString(history_state), indent, is_current); } void ResetBeforeTestRun(WebView* view) { WebFrameImpl* webframe = static_cast(view->mainFrame()); WebCore::Frame* frame = webframe->frame(); // Reset the main frame name since tests always expect it to be empty. It // is normally not reset between page loads (even in IE and FF). if (frame && frame->tree()) frame->tree()->setName(WebCore::emptyAtom); // This is papering over b/850700. But it passes a few more tests, so we'll // keep it for now. if (frame && frame->script()) frame->script()->setEventHandlerLineNumber(0); #if defined(OS_WIN) // Reset the last click information so the clicks generated from previous // test aren't inherited (otherwise can mistake single/double/triple clicks) WebKit::WebInputEventFactory::resetLastClickState(); #endif } #ifndef NDEBUG // The log macro was having problems due to collisions with WTF, so we just // code here what that would have inlined. void DumpLeakedObject(const char* file, int line, const char* object, int count) { std::string msg = StringPrintf("%s LEAKED %d TIMES", object, count); AppendToLog(file, line, msg.c_str()); } #endif void CheckForLeaks() { #ifndef NDEBUG int count = WebFrameImpl::liveObjectCount(); if (count) DumpLeakedObject(__FILE__, __LINE__, "WebFrame", count); #endif } bool DecodeImage(const std::string& image_data, SkBitmap* image) { WebKit::WebData web_data(image_data.data(), image_data.length()); WebKit::WebImage web_image(WebKit::WebImage::fromData(web_data, WebKit::WebSize())); if (web_image.isNull()) return false; #if defined(OS_MACOSX) *image = gfx::CGImageToSkBitmap(web_image.getCGImageRef()); #else *image = web_image.getSkBitmap(); #endif return true; } // NOTE: This pair of conversion functions are here instead of in glue_util.cc // since that file will eventually die. This pair of functions will need to // remain as the concept of a file-path specific character encoding string type // will most likely not make its way into WebKit. FilePath::StringType WebStringToFilePathString(const WebString& str) { #if defined(OS_POSIX) return base::SysWideToNativeMB(UTF16ToWideHack(str)); #elif defined(OS_WIN) return UTF16ToWideHack(str); #endif } WebString FilePathStringToWebString(const FilePath::StringType& str) { #if defined(OS_POSIX) return WideToUTF16Hack(base::SysNativeMBToWide(str)); #elif defined(OS_WIN) return WideToUTF16Hack(str); #endif } FilePath WebStringToFilePath(const WebKit::WebString& str) { return FilePath(WebStringToFilePathString(str)); } WebKit::WebString FilePathToWebString(const FilePath& file_path) { return FilePathStringToWebString(file_path.value()); } std::string GetWebKitVersion() { return StringPrintf("%d.%d", WEBKIT_VERSION_MAJOR, WEBKIT_VERSION_MINOR); } namespace { struct UserAgentState { UserAgentState() : user_agent_requested(false), user_agent_is_overridden(false) { } std::string user_agent; // The UA string when we're pretending to be Chrome 1. std::string mimic_chrome1_user_agent; // The UA string when we're pretending to be Windows Chrome. std::string mimic_windows_user_agent; bool user_agent_requested; bool user_agent_is_overridden; }; Singleton g_user_agent; std::string BuildOSCpuInfo() { std::string os_cpu; #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) int32 os_major_version = 0; int32 os_minor_version = 0; int32 os_bugfix_version = 0; base::SysInfo::OperatingSystemVersionNumbers(&os_major_version, &os_minor_version, &os_bugfix_version); #endif #if defined(OS_POSIX) && !defined(OS_MACOSX) // Should work on any Posix system. struct utsname unixinfo; uname(&unixinfo); std::string cputype; // special case for biarch systems if (strcmp(unixinfo.machine, "x86_64") == 0 && sizeof(void*) == sizeof(int32)) { cputype.assign("i686 (x86_64)"); } else { cputype.assign(unixinfo.machine); } #endif StringAppendF( &os_cpu, #if defined(OS_WIN) "Windows NT %d.%d", os_major_version, os_minor_version #elif defined(OS_MACOSX) "Intel Mac OS X %d_%d_%d", os_major_version, os_minor_version, os_bugfix_version #elif defined(OS_CHROMEOS) "CrOS %s %d.%d.%d", cputype.c_str(), // e.g. i686 os_major_version, os_minor_version, os_bugfix_version #else "%s %s", unixinfo.sysname, // e.g. Linux cputype.c_str() // e.g. i686 #endif ); return os_cpu; } // Construct the User-Agent header, filling in |result|. // The other parameters are workarounds for broken websites: // - If mimic_chrome1 is true, produce a fake Chrome 1 string. // - If mimic_windows is true, produce a fake Windows Chrome string. void BuildUserAgent(bool mimic_chrome1, bool mimic_windows, std::string* result) { const char kUserAgentPlatform[] = #if defined(OS_WIN) "Windows"; #elif defined(OS_MACOSX) "Macintosh"; #elif defined(USE_X11) "X11"; // strange, but that's what Firefox uses #else "?"; #endif const char kUserAgentSecurity = 'U'; // "US" strength encryption // TODO(port): figure out correct locale const char kUserAgentLocale[] = "en-US"; // Get the product name and version, and replace Safari's Version/X string // with it. This is done to expose our product name in a manner that is // maximally compatible with Safari, we hope!! std::string product; if (mimic_chrome1) { product = kChrome1ProductString; } else { scoped_ptr version_info( FileVersionInfo::CreateFileVersionInfoForCurrentModule()); if (version_info.get()) { product = "Chrome/" + WideToASCII(version_info->product_version()); } else { DLOG(WARNING) << "Unknown product version"; product = "Chrome/0.0.0.0"; } } // Derived from Safari's UA string. StringAppendF( result, "Mozilla/5.0 (%s; %c; %s; %s) AppleWebKit/%d.%d" " (KHTML, like Gecko) %s Safari/%d.%d", mimic_windows ? "Windows" : kUserAgentPlatform, kUserAgentSecurity, ((mimic_windows ? "Windows " : "") + BuildOSCpuInfo()).c_str(), kUserAgentLocale, WEBKIT_VERSION_MAJOR, WEBKIT_VERSION_MINOR, product.c_str(), WEBKIT_VERSION_MAJOR, WEBKIT_VERSION_MINOR ); } void SetUserAgentToDefault() { BuildUserAgent(false, false, &g_user_agent->user_agent); } } // namespace void SetUserAgent(const std::string& new_user_agent) { // If you combine this with the previous line, the function only works the // first time. DCHECK(!g_user_agent->user_agent_requested) << "Setting the user agent after someone has " "already requested it can result in unexpected behavior."; g_user_agent->user_agent_is_overridden = true; g_user_agent->user_agent = new_user_agent; } const std::string& GetUserAgent(const GURL& url) { if (g_user_agent->user_agent.empty()) SetUserAgentToDefault(); g_user_agent->user_agent_requested = true; if (!g_user_agent->user_agent_is_overridden) { // Workarounds for sites that use misguided UA sniffing. if (MatchPatternASCII(url.host(), "*.pointroll.com")) { // For cnn.com, which uses pointroll.com to serve their front door promo, // we must spoof Chrome 1.0 in order to avoid a blank page. // http://crbug.com/25934 // TODO(dglazkov): Remove this once CNN's front door promo is // over or when pointroll fixes their sniffing. if (g_user_agent->mimic_chrome1_user_agent.empty()) BuildUserAgent(true, false, &g_user_agent->mimic_chrome1_user_agent); return g_user_agent->mimic_chrome1_user_agent; } #if defined(OS_POSIX) && !defined(OS_MACOSX) else if (MatchPatternASCII(url.host(), "*.mail.yahoo.com")) { // mail.yahoo.com is ok with Windows Chrome but not Linux Chrome. // http://bugs.chromium.org/11136 // TODO(evanm): remove this if Yahoo fixes their sniffing. if (g_user_agent->mimic_windows_user_agent.empty()) BuildUserAgent(false, true, &g_user_agent->mimic_windows_user_agent); return g_user_agent->mimic_windows_user_agent; } #endif } return g_user_agent->user_agent; } void SetForcefullyTerminatePluginProcess(bool value) { if (IsPluginRunningInRendererProcess()) { // Ignore this quirk when the plugins are not running in their own process. return; } g_forcefully_terminate_plugin_process = value; } bool ShouldForcefullyTerminatePluginProcess() { return g_forcefully_terminate_plugin_process; } WebCanvas* ToWebCanvas(skia::PlatformCanvas* canvas) { #if WEBKIT_USING_SKIA return canvas; #elif WEBKIT_USING_CG return canvas->getTopPlatformDevice().GetBitmapContext(); #else NOTIMPLEMENTED(); return NULL; #endif } int GetGlyphPageCount() { return WebCore::GlyphPageTreeNode::treeGlyphPageCount(); } bool g_enable_media_cache = false; bool IsMediaCacheEnabled() { return g_enable_media_cache; } void SetMediaCacheEnabled(bool enabled) { g_enable_media_cache = enabled; } } // namespace webkit_glue