Home | History | Annotate | Download | only in glue
      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