1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "webkit/glue/webkit_glue.h" 6 7#if defined(OS_WIN) 8#include <objidl.h> 9#include <mlang.h> 10#elif defined(OS_POSIX) && !defined(OS_MACOSX) 11#include <sys/utsname.h> 12#endif 13 14#include "base/lazy_instance.h" 15#include "base/logging.h" 16#include "base/memory/scoped_ptr.h" 17#include "base/string_piece.h" 18#include "base/string_tokenizer.h" 19#include "base/string_util.h" 20#include "base/stringprintf.h" 21#include "base/sys_info.h" 22#include "base/sys_string_conversions.h" 23#include "base/utf_string_conversions.h" 24#include "net/base/escape.h" 25#include "skia/ext/platform_canvas.h" 26#if defined(OS_MACOSX) 27#include "skia/ext/skia_utils_mac.h" 28#endif 29#include "third_party/skia/include/core/SkBitmap.h" 30#include "third_party/WebKit/Source/WebKit/chromium/public/WebData.h" 31#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" 32#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" 33#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" 34#include "third_party/WebKit/Source/WebKit/chromium/public/WebGlyphCache.h" 35#include "third_party/WebKit/Source/WebKit/chromium/public/WebHistoryItem.h" 36#include "third_party/WebKit/Source/WebKit/chromium/public/WebImage.h" 37#include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h" 38#include "third_party/WebKit/Source/WebKit/chromium/public/WebSize.h" 39#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" 40#include "third_party/WebKit/Source/WebKit/chromium/public/WebVector.h" 41#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" 42#if defined(OS_WIN) 43#include "third_party/WebKit/Source/WebKit/chromium/public/win/WebInputEventFactory.h" 44#endif 45#include "webkit/glue/glue_serialize.h" 46#include "webkit/glue/user_agent.h" 47#include "v8/include/v8.h" 48 49using WebKit::WebCanvas; 50using WebKit::WebData; 51using WebKit::WebElement; 52using WebKit::WebFrame; 53using WebKit::WebGlyphCache; 54using WebKit::WebHistoryItem; 55using WebKit::WebImage; 56using WebKit::WebSize; 57using WebKit::WebString; 58using WebKit::WebVector; 59using WebKit::WebView; 60 61static const char kLayoutTestsPattern[] = "/LayoutTests/"; 62static const std::string::size_type kLayoutTestsPatternSize = 63 arraysize(kLayoutTestsPattern) - 1; 64static const char kFileUrlPattern[] = "file:/"; 65static const char kDataUrlPattern[] = "data:"; 66static const std::string::size_type kDataUrlPatternSize = 67 arraysize(kDataUrlPattern) - 1; 68static const char kFileTestPrefix[] = "(file test):"; 69 70//------------------------------------------------------------------------------ 71// webkit_glue impl: 72 73namespace webkit_glue { 74 75// Global variable used by the plugin quirk "die after unload". 76bool g_forcefully_terminate_plugin_process = false; 77 78void SetJavaScriptFlags(const std::string& str) { 79#if WEBKIT_USING_V8 80 v8::V8::SetFlagsFromString(str.data(), static_cast<int>(str.size())); 81#endif 82} 83 84void EnableWebCoreLogChannels(const std::string& channels) { 85 if (channels.empty()) 86 return; 87 StringTokenizer t(channels, ", "); 88 while (t.GetNext()) { 89 WebKit::enableLogChannel(t.token().c_str()); 90 } 91} 92 93string16 DumpDocumentText(WebFrame* web_frame) { 94 // We use the document element's text instead of the body text here because 95 // not all documents have a body, such as XML documents. 96 WebElement document_element = web_frame->document().documentElement(); 97 if (document_element.isNull()) 98 return string16(); 99 100 return document_element.innerText(); 101} 102 103string16 DumpFramesAsText(WebFrame* web_frame, bool recursive) { 104 string16 result; 105 106 // Add header for all but the main frame. Skip empty frames. 107 if (web_frame->parent() && 108 !web_frame->document().documentElement().isNull()) { 109 result.append(ASCIIToUTF16("\n--------\nFrame: '")); 110 result.append(web_frame->name()); 111 result.append(ASCIIToUTF16("'\n--------\n")); 112 } 113 114 result.append(DumpDocumentText(web_frame)); 115 result.append(ASCIIToUTF16("\n")); 116 117 if (recursive) { 118 WebFrame* child = web_frame->firstChild(); 119 for (; child; child = child->nextSibling()) 120 result.append(DumpFramesAsText(child, recursive)); 121 } 122 123 return result; 124} 125 126string16 DumpRenderer(WebFrame* web_frame) { 127 return web_frame->renderTreeAsText(); 128} 129 130bool CounterValueForElementById(WebFrame* web_frame, const std::string& id, 131 string16* counter_value) { 132 WebString result = 133 web_frame->counterValueForElementById(WebString::fromUTF8(id)); 134 if (result.isNull()) 135 return false; 136 137 *counter_value = result; 138 return true; 139} 140 141int PageNumberForElementById(WebFrame* web_frame, 142 const std::string& id, 143 float page_width_in_pixels, 144 float page_height_in_pixels) { 145 return web_frame->pageNumberForElementById(WebString::fromUTF8(id), 146 page_width_in_pixels, 147 page_height_in_pixels); 148} 149 150int NumberOfPages(WebFrame* web_frame, 151 float page_width_in_pixels, 152 float page_height_in_pixels) { 153 WebSize size(static_cast<int>(page_width_in_pixels), 154 static_cast<int>(page_height_in_pixels)); 155 int number_of_pages = web_frame->printBegin(size); 156 web_frame->printEnd(); 157 return number_of_pages; 158} 159 160string16 DumpFrameScrollPosition(WebFrame* web_frame, bool recursive) { 161 gfx::Size offset = web_frame->scrollOffset(); 162 std::string result_utf8; 163 164 if (offset.width() > 0 || offset.height() > 0) { 165 if (web_frame->parent()) { 166 base::StringAppendF(&result_utf8, "frame '%s' ", 167 UTF16ToUTF8(web_frame->name()).c_str()); 168 } 169 base::StringAppendF(&result_utf8, "scrolled to %d,%d\n", 170 offset.width(), offset.height()); 171 } 172 173 string16 result = UTF8ToUTF16(result_utf8); 174 175 if (recursive) { 176 WebFrame* child = web_frame->firstChild(); 177 for (; child; child = child->nextSibling()) 178 result.append(DumpFrameScrollPosition(child, recursive)); 179 } 180 181 return result; 182} 183 184// Returns True if item1 < item2. 185static bool HistoryItemCompareLess(const WebHistoryItem& item1, 186 const WebHistoryItem& item2) { 187 string16 target1 = item1.target(); 188 string16 target2 = item2.target(); 189 std::transform(target1.begin(), target1.end(), target1.begin(), tolower); 190 std::transform(target2.begin(), target2.end(), target2.begin(), tolower); 191 return target1 < target2; 192} 193 194// Writes out a HistoryItem into a UTF-8 string in a readable format. 195static std::string DumpHistoryItem(const WebHistoryItem& item, 196 int indent, bool is_current) { 197 std::string result; 198 199 if (is_current) { 200 result.append("curr->"); 201 result.append(indent - 6, ' '); // 6 == "curr->".length() 202 } else { 203 result.append(indent, ' '); 204 } 205 206 std::string url = item.urlString().utf8(); 207 size_t pos; 208 if (url.find(kFileUrlPattern) == 0 && 209 ((pos = url.find(kLayoutTestsPattern)) != std::string::npos)) { 210 // adjust file URLs to match upstream results. 211 url.replace(0, pos + kLayoutTestsPatternSize, kFileTestPrefix); 212 } else if (url.find(kDataUrlPattern) == 0) { 213 // URL-escape data URLs to match results upstream. 214 std::string path = EscapePath(url.substr(kDataUrlPatternSize)); 215 url.replace(kDataUrlPatternSize, url.length(), path); 216 } 217 218 result.append(url); 219 if (!item.target().isEmpty()) 220 result.append(" (in frame \"" + UTF16ToUTF8(item.target()) + "\")"); 221 if (item.isTargetItem()) 222 result.append(" **nav target**"); 223 result.append("\n"); 224 225 const WebVector<WebHistoryItem>& children = item.children(); 226 if (!children.isEmpty()) { 227 // Must sort to eliminate arbitrary result ordering which defeats 228 // reproducible testing. 229 // TODO(darin): WebVector should probably just be a std::vector!! 230 std::vector<WebHistoryItem> sorted_children; 231 for (size_t i = 0; i < children.size(); ++i) 232 sorted_children.push_back(children[i]); 233 std::sort(sorted_children.begin(), sorted_children.end(), 234 HistoryItemCompareLess); 235 for (size_t i = 0; i < sorted_children.size(); i++) 236 result += DumpHistoryItem(sorted_children[i], indent+4, false); 237 } 238 239 return result; 240} 241 242string16 DumpHistoryState(const std::string& history_state, int indent, 243 bool is_current) { 244 return UTF8ToUTF16( 245 DumpHistoryItem(HistoryItemFromString(history_state), indent, 246 is_current)); 247} 248 249#ifndef NDEBUG 250// The log macro was having problems due to collisions with WTF, so we just 251// code here what that would have inlined. 252void DumpLeakedObject(const char* file, int line, const char* object, 253 int count) { 254 std::string msg = base::StringPrintf("%s LEAKED %d TIMES", object, count); 255 AppendToLog(file, line, msg.c_str()); 256} 257#endif 258 259void CheckForLeaks() { 260#ifndef NDEBUG 261 int count = WebFrame::instanceCount(); 262 if (count) 263 DumpLeakedObject(__FILE__, __LINE__, "WebFrame", count); 264#endif 265} 266 267bool DecodeImage(const std::string& image_data, SkBitmap* image) { 268 WebData web_data(image_data.data(), image_data.length()); 269 WebImage web_image(WebImage::fromData(web_data, WebSize())); 270 if (web_image.isNull()) 271 return false; 272 273#if defined(OS_MACOSX) 274 *image = gfx::CGImageToSkBitmap(web_image.getCGImageRef()); 275#else 276 *image = web_image.getSkBitmap(); 277#endif 278 return true; 279} 280 281// NOTE: This pair of conversion functions are here instead of in glue_util.cc 282// since that file will eventually die. This pair of functions will need to 283// remain as the concept of a file-path specific character encoding string type 284// will most likely not make its way into WebKit. 285 286FilePath::StringType WebStringToFilePathString(const WebString& str) { 287#if defined(OS_POSIX) 288 return base::SysWideToNativeMB(UTF16ToWideHack(str)); 289#elif defined(OS_WIN) 290 return UTF16ToWideHack(str); 291#endif 292} 293 294WebString FilePathStringToWebString(const FilePath::StringType& str) { 295#if defined(OS_POSIX) 296 return WideToUTF16Hack(base::SysNativeMBToWide(str)); 297#elif defined(OS_WIN) 298 return WideToUTF16Hack(str); 299#endif 300} 301 302FilePath WebStringToFilePath(const WebString& str) { 303 return FilePath(WebStringToFilePathString(str)); 304} 305 306WebString FilePathToWebString(const FilePath& file_path) { 307 return FilePathStringToWebString(file_path.value()); 308} 309 310WebKit::WebFileError PlatformFileErrorToWebFileError( 311 base::PlatformFileError error_code) { 312 switch (error_code) { 313 case base::PLATFORM_FILE_ERROR_NOT_FOUND: 314 return WebKit::WebFileErrorNotFound; 315 case base::PLATFORM_FILE_ERROR_INVALID_OPERATION: 316 case base::PLATFORM_FILE_ERROR_EXISTS: 317 case base::PLATFORM_FILE_ERROR_NOT_EMPTY: 318 return WebKit::WebFileErrorInvalidModification; 319 case base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY: 320 case base::PLATFORM_FILE_ERROR_NOT_A_FILE: 321 return WebKit::WebFileErrorTypeMismatch; 322 case base::PLATFORM_FILE_ERROR_ACCESS_DENIED: 323 return WebKit::WebFileErrorNoModificationAllowed; 324 case base::PLATFORM_FILE_ERROR_FAILED: 325 return WebKit::WebFileErrorInvalidState; 326 case base::PLATFORM_FILE_ERROR_ABORT: 327 return WebKit::WebFileErrorAbort; 328 case base::PLATFORM_FILE_ERROR_SECURITY: 329 return WebKit::WebFileErrorSecurity; 330 case base::PLATFORM_FILE_ERROR_NO_SPACE: 331 return WebKit::WebFileErrorQuotaExceeded; 332 default: 333 return WebKit::WebFileErrorInvalidModification; 334 } 335} 336 337namespace { 338 339struct UserAgentState { 340 UserAgentState() 341 : user_agent_requested(false), 342 user_agent_is_overridden(false) { 343 } 344 345 std::string user_agent; 346 347 // The UA string when we're pretending to be Windows Chrome. 348 std::string mimic_windows_user_agent; 349 350 bool user_agent_requested; 351 bool user_agent_is_overridden; 352}; 353 354static base::LazyInstance<UserAgentState> g_user_agent( 355 base::LINKER_INITIALIZED); 356 357void SetUserAgentToDefault() { 358 BuildUserAgent(false, &g_user_agent.Get().user_agent); 359} 360 361} // namespace 362 363void SetUserAgent(const std::string& new_user_agent) { 364 // If you combine this with the previous line, the function only works the 365 // first time. 366 DCHECK(!g_user_agent.Get().user_agent_requested) << 367 "Setting the user agent after someone has " 368 "already requested it can result in unexpected behavior."; 369 g_user_agent.Get().user_agent_is_overridden = true; 370 g_user_agent.Get().user_agent = new_user_agent; 371} 372 373const std::string& GetUserAgent(const GURL& url) { 374 if (g_user_agent.Get().user_agent.empty()) 375 SetUserAgentToDefault(); 376 g_user_agent.Get().user_agent_requested = true; 377 if (!g_user_agent.Get().user_agent_is_overridden) { 378 // Workarounds for sites that use misguided UA sniffing. 379#if defined(OS_POSIX) && !defined(OS_MACOSX) 380 if (MatchPattern(url.host(), "*.mail.yahoo.com")) { 381 // mail.yahoo.com is ok with Windows Chrome but not Linux Chrome. 382 // http://bugs.chromium.org/11136 383 // TODO(evanm): remove this if Yahoo fixes their sniffing. 384 if (g_user_agent.Get().mimic_windows_user_agent.empty()) 385 BuildUserAgent(true, &g_user_agent.Get().mimic_windows_user_agent); 386 return g_user_agent.Get().mimic_windows_user_agent; 387 } 388#endif 389 } 390 return g_user_agent.Get().user_agent; 391} 392 393void SetForcefullyTerminatePluginProcess(bool value) { 394 if (IsPluginRunningInRendererProcess()) { 395 // Ignore this quirk when the plugins are not running in their own process. 396 return; 397 } 398 399 g_forcefully_terminate_plugin_process = value; 400} 401 402bool ShouldForcefullyTerminatePluginProcess() { 403 return g_forcefully_terminate_plugin_process; 404} 405 406WebCanvas* ToWebCanvas(skia::PlatformCanvas* canvas) { 407#if WEBKIT_USING_SKIA 408 return canvas; 409#elif WEBKIT_USING_CG 410 return canvas->getTopPlatformDevice().GetBitmapContext(); 411#else 412 NOTIMPLEMENTED(); 413 return NULL; 414#endif 415} 416 417int GetGlyphPageCount() { 418 return WebGlyphCache::pageCount(); 419} 420 421} // namespace webkit_glue 422