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 49 using WebKit::WebCanvas; 50 using WebKit::WebData; 51 using WebKit::WebElement; 52 using WebKit::WebFrame; 53 using WebKit::WebGlyphCache; 54 using WebKit::WebHistoryItem; 55 using WebKit::WebImage; 56 using WebKit::WebSize; 57 using WebKit::WebString; 58 using WebKit::WebVector; 59 using WebKit::WebView; 60 61 static const char kLayoutTestsPattern[] = "/LayoutTests/"; 62 static const std::string::size_type kLayoutTestsPatternSize = 63 arraysize(kLayoutTestsPattern) - 1; 64 static const char kFileUrlPattern[] = "file:/"; 65 static const char kDataUrlPattern[] = "data:"; 66 static const std::string::size_type kDataUrlPatternSize = 67 arraysize(kDataUrlPattern) - 1; 68 static const char kFileTestPrefix[] = "(file test):"; 69 70 //------------------------------------------------------------------------------ 71 // webkit_glue impl: 72 73 namespace webkit_glue { 74 75 // Global variable used by the plugin quirk "die after unload". 76 bool g_forcefully_terminate_plugin_process = false; 77 78 void 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 84 void 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 93 string16 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 103 string16 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 126 string16 DumpRenderer(WebFrame* web_frame) { 127 return web_frame->renderTreeAsText(); 128 } 129 130 bool 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 141 int 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 150 int 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 160 string16 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. 185 static 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. 195 static 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 242 string16 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. 252 void 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 259 void CheckForLeaks() { 260 #ifndef NDEBUG 261 int count = WebFrame::instanceCount(); 262 if (count) 263 DumpLeakedObject(__FILE__, __LINE__, "WebFrame", count); 264 #endif 265 } 266 267 bool 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 286 FilePath::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 294 WebString 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 302 FilePath WebStringToFilePath(const WebString& str) { 303 return FilePath(WebStringToFilePathString(str)); 304 } 305 306 WebString FilePathToWebString(const FilePath& file_path) { 307 return FilePathStringToWebString(file_path.value()); 308 } 309 310 WebKit::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 337 namespace { 338 339 struct 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 354 static base::LazyInstance<UserAgentState> g_user_agent( 355 base::LINKER_INITIALIZED); 356 357 void SetUserAgentToDefault() { 358 BuildUserAgent(false, &g_user_agent.Get().user_agent); 359 } 360 361 } // namespace 362 363 void 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 373 const 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 393 void 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 402 bool ShouldForcefullyTerminatePluginProcess() { 403 return g_forcefully_terminate_plugin_process; 404 } 405 406 WebCanvas* 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 417 int GetGlyphPageCount() { 418 return WebGlyphCache::pageCount(); 419 } 420 421 } // namespace webkit_glue 422