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