• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SetJavaScriptFlags(const std::string & str)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 
EnableWebCoreLogChannels(const std::string & channels)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 
DumpDocumentText(WebFrame * web_frame)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 
DumpFramesAsText(WebFrame * web_frame,bool recursive)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 
DumpRenderer(WebFrame * web_frame)126 string16 DumpRenderer(WebFrame* web_frame) {
127   return web_frame->renderTreeAsText();
128 }
129 
CounterValueForElementById(WebFrame * web_frame,const std::string & id,string16 * counter_value)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 
PageNumberForElementById(WebFrame * web_frame,const std::string & id,float page_width_in_pixels,float page_height_in_pixels)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 
NumberOfPages(WebFrame * web_frame,float page_width_in_pixels,float page_height_in_pixels)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 
DumpFrameScrollPosition(WebFrame * web_frame,bool recursive)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.
HistoryItemCompareLess(const WebHistoryItem & item1,const WebHistoryItem & 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.
DumpHistoryItem(const WebHistoryItem & item,int indent,bool is_current)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 
DumpHistoryState(const std::string & history_state,int indent,bool is_current)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.
DumpLeakedObject(const char * file,int line,const char * object,int count)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 
CheckForLeaks()259 void CheckForLeaks() {
260 #ifndef NDEBUG
261   int count = WebFrame::instanceCount();
262   if (count)
263     DumpLeakedObject(__FILE__, __LINE__, "WebFrame", count);
264 #endif
265 }
266 
DecodeImage(const std::string & image_data,SkBitmap * image)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 
WebStringToFilePathString(const WebString & str)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 
FilePathStringToWebString(const FilePath::StringType & str)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 
WebStringToFilePath(const WebString & str)302 FilePath WebStringToFilePath(const WebString& str) {
303   return FilePath(WebStringToFilePathString(str));
304 }
305 
FilePathToWebString(const FilePath & file_path)306 WebString FilePathToWebString(const FilePath& file_path) {
307   return FilePathStringToWebString(file_path.value());
308 }
309 
PlatformFileErrorToWebFileError(base::PlatformFileError error_code)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 {
UserAgentStatewebkit_glue::__anon942cbb250111::UserAgentState340   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 
SetUserAgentToDefault()357 void SetUserAgentToDefault() {
358   BuildUserAgent(false, &g_user_agent.Get().user_agent);
359 }
360 
361 }  // namespace
362 
SetUserAgent(const std::string & new_user_agent)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 
GetUserAgent(const GURL & url)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 
SetForcefullyTerminatePluginProcess(bool value)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 
ShouldForcefullyTerminatePluginProcess()402 bool ShouldForcefullyTerminatePluginProcess() {
403   return g_forcefully_terminate_plugin_process;
404 }
405 
ToWebCanvas(skia::PlatformCanvas * canvas)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 
GetGlyphPageCount()417 int GetGlyphPageCount() {
418   return WebGlyphCache::pageCount();
419 }
420 
421 } // namespace webkit_glue
422