diff options
-rw-r--r-- | build/SConscript.main | 1 | ||||
-rw-r--r-- | webkit/port/platform/graphics/chromium/FontCacheLinux.cpp | 22 | ||||
-rw-r--r-- | webkit/port/platform/graphics/chromium/FontLinux.cpp | 56 | ||||
-rw-r--r-- | webkit/port/platform/graphics/chromium/FontPlatformDataLinux.cpp | 136 | ||||
-rw-r--r-- | webkit/port/platform/graphics/chromium/FontPlatformDataLinux.h | 52 | ||||
-rw-r--r-- | webkit/port/platform/graphics/chromium/GlyphPageTreeNodeLinux.cpp | 35 | ||||
-rw-r--r-- | webkit/port/platform/graphics/chromium/SimpleFontDataLinux.cpp | 67 | ||||
-rw-r--r-- | webkit/tools/test_shell/SConscript | 2 | ||||
-rw-r--r-- | webkit/tools/test_shell/event_sending_controller.cc | 6 | ||||
-rw-r--r-- | webkit/tools/test_shell/gtk/test_shell.cc | 283 | ||||
-rw-r--r-- | webkit/tools/test_shell/gtk/test_webview_delegate.cc | 5 | ||||
-rw-r--r-- | webkit/tools/test_shell/gtk/webview_host.cc | 13 | ||||
-rw-r--r-- | webkit/tools/test_shell/gtk/webwidget_host.cc | 52 | ||||
-rw-r--r-- | webkit/tools/test_shell/test_shell_main_gtk.cc | 3 | ||||
-rw-r--r-- | webkit/tools/test_shell/webwidget_host.h | 10 |
15 files changed, 653 insertions, 90 deletions
diff --git a/build/SConscript.main b/build/SConscript.main index 0bab58b..3c3903d 100644 --- a/build/SConscript.main +++ b/build/SConscript.main @@ -490,6 +490,7 @@ if ARGUMENTS.get('COVERAGE') == '1': if root_env['PLATFORM'] in ['linux', 'linux2', 'posix']: linux_env.ParseConfig('pkg-config --cflags --libs nss') linux_env.ParseConfig('pkg-config --cflags --libs gtk+-2.0') + linux_env.ParseConfig('pkg-config --cflags --libs pangoft2') # -------------------------------------------------------------------------- diff --git a/webkit/port/platform/graphics/chromium/FontCacheLinux.cpp b/webkit/port/platform/graphics/chromium/FontCacheLinux.cpp index 6630f670..2163c45 100644 --- a/webkit/port/platform/graphics/chromium/FontCacheLinux.cpp +++ b/webkit/port/platform/graphics/chromium/FontCacheLinux.cpp @@ -4,16 +4,20 @@ #include "config.h" #include "FontCache.h" -#include "AtomicString.h" +#include "AtomicString.h" +#include "FontDescription.h" +#include "FontPlatformData.h" +#include "Logging.h" #include "NotImplemented.h" namespace WebCore { -// TODO(agl): stubs only - - -void FontCache::platformInit() { } +void FontCache::platformInit() +{ + if (!FontPlatformData::init()) + ASSERT_NOT_REACHED(); +} const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, @@ -35,13 +39,14 @@ const AtomicString& FontCache::alternateFamilyName(const AtomicString& familyNam FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) { + notImplemented(); return 0; } FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& description) { - notImplemented(); - return 0; + static AtomicString arialStr("Arial"); + return getCachedFontPlatformData(description, arialStr); } void FontCache::getTraitsInFamily(const AtomicString& familyName, @@ -53,8 +58,7 @@ void FontCache::getTraitsInFamily(const AtomicString& familyName, FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) { - notImplemented(); - return 0; + return new FontPlatformData(fontDescription, family); } AtomicString FontCache::getGenericFontForScript(UScriptCode script, diff --git a/webkit/port/platform/graphics/chromium/FontLinux.cpp b/webkit/port/platform/graphics/chromium/FontLinux.cpp index 9ee965f..2c186d1 100644 --- a/webkit/port/platform/graphics/chromium/FontLinux.cpp +++ b/webkit/port/platform/graphics/chromium/FontLinux.cpp @@ -5,18 +5,64 @@ #include "config.h" #include "Font.h" +#include <pango/pango.h> +#include <pango/pangoft2.h> + #include "FloatRect.h" #include "NotImplemented.h" +#include "PlatformContextSkia.h" +#include "GraphicsContext.h" +#include "SimpleFontData.h" +#include "GlyphBuffer.h" namespace WebCore { -// TODO(agl): stubs only +// ----------------------------------------------------------------------------- +// Bitblit a Freetype bitmap onto a canvas at the given location in the given +// colour. +// pgc: the Skia canvas +// bm: A freetype bitmap which is an 8-bit alpha bitmap +// ----------------------------------------------------------------------------- +static void bitBlitAlpha(PlatformGraphicsContext* pgc, FT_Bitmap* bm, + int x, int y, const Color& col) +{ + SkPaint paint; + paint.setARGB(col.alpha(), col.red(), col.green(), col.blue()); + + // Here we are constructing an SkBitmap by pointing directly into the + // Freetype bitmap data + SkBitmap glyph; + glyph.setConfig(SkBitmap::kA8_Config, bm->width, bm->rows, bm->pitch); + glyph.setPixels(bm->buffer); + pgc->canvas()->drawBitmap(glyph, x, y, &paint); +} -void Font::drawGlyphs(GraphicsContext*, const SimpleFontData*, - const GlyphBuffer&, int from, int to, - const FloatPoint&) const +void Font::drawGlyphs(GraphicsContext* ctx, const SimpleFontData* sfd, + const GlyphBuffer& glyphBuffer, int from, int to, + const FloatPoint& point) const { - notImplemented(); + // For now we draw text by getting the Freetype face from Pango and asking + // Freetype to render each glyph as an 8-bit alpha bitmap and drawing that + // to the canvas. This, obviously, ignores kerning, ligatures and other + // things that we should have in the real version. + GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from); + PlatformGraphicsContext* pgc = ctx->platformContext(); + FT_Face face = pango_ft2_font_get_face(sfd->m_font.m_font); + FT_GlyphSlot slot = face->glyph; + + int x = point.x(), y = point.y(); + + for (int i = from; i < to; ++i) { + const FT_Error error = FT_Load_Glyph(face, glyphs[i], FT_LOAD_RENDER); + if (error) + continue; + + bitBlitAlpha(pgc, &slot->bitmap, x + slot->bitmap_left, + y - slot->bitmap_top, ctx->fillColor()); + // Freetype works in 1/64ths of a pixel, so we divide by 64 to get the + // number of pixels to advance. + x += slot->advance.x >> 6; + } } void Font::drawComplexText(GraphicsContext* context, const TextRun& run, diff --git a/webkit/port/platform/graphics/chromium/FontPlatformDataLinux.cpp b/webkit/port/platform/graphics/chromium/FontPlatformDataLinux.cpp index 855d6d8..e14a023 100644 --- a/webkit/port/platform/graphics/chromium/FontPlatformDataLinux.cpp +++ b/webkit/port/platform/graphics/chromium/FontPlatformDataLinux.cpp @@ -5,51 +5,133 @@ #include "config.h" #include "FontPlatformData.h" +#include <gtk/gtk.h> +#include <pango/pango.h> +#include <pango/pangoft2.h> + +#include "CString.h" +#include "FontDescription.h" #include "NotImplemented.h" +#include "PlatformString.h" namespace WebCore { -// TODO(agl): stubs only +PangoFontMap* FontPlatformData::m_fontMap = 0; +GHashTable* FontPlatformData::m_hashTable = 0; -FontPlatformData::FontPlatformData() { - notImplemented(); -} +FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const AtomicString& familyName) + : m_context(0) + , m_font(0) + , m_size(fontDescription.computedSize()) + , m_syntheticBold(false) + , m_syntheticOblique(false) +{ + FontPlatformData::init(); -FontPlatformData::FontPlatformData(WTF::HashTableDeletedValueType) { - notImplemented(); -} + CString stored_family = familyName.string().utf8(); + char const* families[] = { + stored_family.data(), + NULL + }; -FontPlatformData::FontPlatformData(const FontDescription& fontDescription, - const AtomicString& familyName) { - notImplemented(); -} + switch (fontDescription.genericFamily()) { + case FontDescription::SerifFamily: + families[1] = "serif"; + break; + case FontDescription::SansSerifFamily: + families[1] = "sans"; + break; + case FontDescription::MonospaceFamily: + families[1] = "monospace"; + break; + case FontDescription::NoFamily: + case FontDescription::StandardFamily: + default: + families[1] = "sans"; + break; + } + + PangoFontDescription* description = pango_font_description_new(); + pango_font_description_set_absolute_size(description, fontDescription.computedSize() * PANGO_SCALE); + + // FIXME: Map all FontWeight values to Pango font weights. + if (fontDescription.weight() >= FontWeight600) + pango_font_description_set_weight(description, PANGO_WEIGHT_BOLD); + if (fontDescription.italic()) + pango_font_description_set_style(description, PANGO_STYLE_ITALIC); + + m_context = pango_ft2_font_map_create_context(PANGO_FT2_FONT_MAP(m_fontMap)); + + for (unsigned int i = 0; !m_font && i < G_N_ELEMENTS(families); i++) { + pango_font_description_set_family(description, families[i]); + pango_context_set_font_description(m_context, description); + m_font = pango_font_map_load_font(m_fontMap, m_context, description); + } -FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) { - notImplemented(); + pango_font_description_free(description); } -FontPlatformData::~FontPlatformData() { - notImplemented(); +FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) + : m_context(0) + , m_font(0) + , m_size(size) + , m_syntheticBold(bold) + , m_syntheticOblique(oblique) +{ } -bool FontPlatformData::isHashTableDeletedValue() const { - notImplemented(); - return false; +bool FontPlatformData::init() +{ + static bool initialized = false; + if (initialized) + return true; + initialized = true; + + if (!m_fontMap) + m_fontMap = pango_ft2_font_map_new(); + if (!m_hashTable) { + PangoFontFamily** families = 0; + int n_families = 0; + + m_hashTable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref); + + pango_font_map_list_families(m_fontMap, &families, &n_families); + + for (int family = 0; family < n_families; family++) + g_hash_table_insert(m_hashTable, + g_strdup(pango_font_family_get_name(families[family])), + g_object_ref(families[family])); + + g_free(families); + } + + return true; } -unsigned FontPlatformData::hash() const { - notImplemented(); - return 0; +bool FontPlatformData::isFixedPitch() const +{ + PangoFontDescription* description = pango_font_describe_with_absolute_size(m_font); + PangoFontFamily* family = reinterpret_cast<PangoFontFamily*>(g_hash_table_lookup(m_hashTable, pango_font_description_get_family(description))); + pango_font_description_free(description); + return pango_font_family_is_monospace(family); } -bool FontPlatformData::operator==(const FontPlatformData &other) const { - notImplemented(); - return false; +FontPlatformData::~FontPlatformData() { } -float FontPlatformData::size() const { - notImplemented(); - return 0; +bool FontPlatformData::operator==(const FontPlatformData& other) const +{ + if (m_font == other.m_font) + return true; + if (m_font == 0 || m_font == reinterpret_cast<PangoFont*>(-1) + || other.m_font == 0 || other.m_font == reinterpret_cast<PangoFont*>(-1)) + return false; + PangoFontDescription* thisDesc = pango_font_describe(m_font); + PangoFontDescription* otherDesc = pango_font_describe(other.m_font); + bool result = pango_font_description_equal(thisDesc, otherDesc); + pango_font_description_free(otherDesc); + pango_font_description_free(thisDesc); + return result; } } // namespace WebCore diff --git a/webkit/port/platform/graphics/chromium/FontPlatformDataLinux.h b/webkit/port/platform/graphics/chromium/FontPlatformDataLinux.h index 383fbf3..b8ae94f 100644 --- a/webkit/port/platform/graphics/chromium/FontPlatformDataLinux.h +++ b/webkit/port/platform/graphics/chromium/FontPlatformDataLinux.h @@ -8,7 +8,10 @@ #include "config.h" #include "build/build_config.h" +#include <pango/pango.h> + #include "StringImpl.h" +#include "NotImplemented.h" #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> @@ -16,20 +19,55 @@ namespace WebCore { class FontDescription; -// TODO(agl): stubs only - class FontPlatformData { public: - FontPlatformData(); - FontPlatformData(WTF::HashTableDeletedValueType); + // Used for deleted values in the font cache's hash tables. The hash table + // will create us with this structure, and it will compare other values + // to this "Deleted" one. It expects the Deleted one to be differentiable + // from the NULL one (created with the empty constructor), so we can't just + // set everything to NULL. + FontPlatformData(WTF::HashTableDeletedValueType) + : m_context(0) + , m_font(hashTableDeletedFontValue()) + { } + + FontPlatformData() + : m_context(0) + , m_font(0) + { } + FontPlatformData(const FontDescription&, const AtomicString& family); + FontPlatformData(float size, bool bold, bool oblique); + ~FontPlatformData(); - bool isHashTableDeletedValue() const; - unsigned hash() const; + static bool init(); + + bool isFixedPitch() const; + float size() const { return m_size; } + + unsigned hash() const + { + notImplemented(); + return 0; + } + bool operator==(const FontPlatformData& other) const; - float size() const; + bool isHashTableDeletedValue() const { return m_font == hashTableDeletedFontValue(); } + + static PangoFontMap* m_fontMap; + static GHashTable* m_hashTable; + + PangoContext* m_context; + PangoFont* m_font; + + float m_size; + bool m_syntheticBold; + bool m_syntheticOblique; + +private: + static PangoFont* hashTableDeletedFontValue() { return reinterpret_cast<PangoFont*>(-1); } }; } // namespace WebCore diff --git a/webkit/port/platform/graphics/chromium/GlyphPageTreeNodeLinux.cpp b/webkit/port/platform/graphics/chromium/GlyphPageTreeNodeLinux.cpp index 8dece07..1e7eb79 100644 --- a/webkit/port/platform/graphics/chromium/GlyphPageTreeNodeLinux.cpp +++ b/webkit/port/platform/graphics/chromium/GlyphPageTreeNodeLinux.cpp @@ -28,19 +28,46 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "config.h" +#include "GlyphPageTreeNode.h" + +// This PANGO_ENABLE_BACKEND define lets us get at some of the internal Pango +// call which we need. This include must be here otherwise we include pango.h +// via another route (without the define) and that sets the include guard. +// Then, when we try to include it in the future the guard stops us getting the +// functions that we need. +#define PANGO_ENABLE_BACKEND +#include <pango/pango.h> +#include <pango/pangofc-font.h> #include "Font.h" -#include "GlyphPageTreeNode.h" #include "NotImplemented.h" #include "SimpleFontData.h" namespace WebCore { -bool GlyphPage::fill(unsigned offset, unsigned length, UChar* characterBuffer, unsigned bufferLength, const SimpleFontData* fontData) +bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) { - notImplemented(); - return false; + // The bufferLength will be greater than the glyph page size if the buffer has Unicode supplementary characters. + // We won't support this for now. + if (bufferLength > GlyphPage::size) + return false; + + if (!fontData->m_font.m_font || fontData->m_font.m_font == reinterpret_cast<PangoFont*>(-1)) + return false; + + bool haveGlyphs = false; + for (unsigned i = 0; i < length; i++) { + Glyph glyph = pango_fc_font_get_glyph(PANGO_FC_FONT(fontData->m_font.m_font), buffer[i]); + if (!glyph) + setGlyphDataForIndex(offset + i, 0, 0); + else { + setGlyphDataForIndex(offset + i, glyph, fontData); + haveGlyphs = true; + } + } + + return haveGlyphs; } } // namespace WebCore diff --git a/webkit/port/platform/graphics/chromium/SimpleFontDataLinux.cpp b/webkit/port/platform/graphics/chromium/SimpleFontDataLinux.cpp index 4525bd3..26caef8 100644 --- a/webkit/port/platform/graphics/chromium/SimpleFontDataLinux.cpp +++ b/webkit/port/platform/graphics/chromium/SimpleFontDataLinux.cpp @@ -2,19 +2,53 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define PANGO_ENABLE_BACKEND + #include "config.h" +#include "SimpleFontData.h" + +#include <pango/pango.h> +#include <pango/pangoft2.h> +#include <pango/pangofc-font.h> + #include "Font.h" #include "FontCache.h" -#include "SimpleFontData.h" #include "FloatRect.h" #include "FontDescription.h" +#include "Logging.h" #include "NotImplemented.h" namespace WebCore { // TODO(agl): only stubs -void SimpleFontData::platformInit() { } +void SimpleFontData::platformInit() +{ + PangoFont *const font = platformData().m_font; + + PangoFontMetrics *const metrics = pango_font_get_metrics(font, NULL); + m_ascent = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE; + m_descent = pango_font_metrics_get_descent(metrics) / PANGO_SCALE; + m_lineSpacing = m_ascent + m_descent; + m_avgCharWidth = pango_font_metrics_get_approximate_char_width(metrics) / PANGO_SCALE; + pango_font_metrics_unref(metrics); + + const guint xglyph = pango_fc_font_get_glyph(PANGO_FC_FONT(font), 'x'); + const guint spaceglyph = pango_fc_font_get_glyph(PANGO_FC_FONT(font), ' '); + PangoRectangle rect; + + pango_font_get_glyph_extents(font, xglyph, &rect, NULL); + m_xHeight = rect.height / PANGO_SCALE; + pango_font_get_glyph_extents(font, spaceglyph, NULL, &rect); + m_spaceWidth = rect.width / PANGO_SCALE; + m_lineGap = m_lineSpacing - m_ascent - m_descent; + + FT_Face face = pango_ft2_font_get_face(font); + m_unitsPerEm = face->units_per_EM / PANGO_SCALE; + + // TODO(agl): I'm not sure we have good data for this so it's 0 for now + m_maxCharWidth = 0; +} void SimpleFontData::platformDestroy() { } @@ -27,19 +61,38 @@ SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDes bool SimpleFontData::containsCharacters(const UChar* characters, int length) const { - notImplemented(); - return false; + bool result = true; + + PangoCoverage* requested = pango_coverage_from_bytes((guchar*)characters, length); + PangoCoverage* available = pango_font_get_coverage(m_font.m_font, pango_language_get_default()); + pango_coverage_max(requested, available); + + for (int i = 0; i < length; i++) { + if (PANGO_COVERAGE_NONE == pango_coverage_get(requested, i)) { + result = false; + break; + } + } + + pango_coverage_unref(requested); + pango_coverage_unref(available); + + return result; } void SimpleFontData::determinePitch() { - notImplemented(); + m_treatAsFixedPitch = platformData().isFixedPitch(); } float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { - notImplemented(); - return 0; + PangoFont *const font = platformData().m_font; + PangoRectangle rect; + + pango_font_get_glyph_extents(font, glyph, NULL, &rect); + + return static_cast<float>(rect.width) / PANGO_SCALE; } } // namespace WebCore diff --git a/webkit/tools/test_shell/SConscript b/webkit/tools/test_shell/SConscript index 92e9d0d..30c4f96 100644 --- a/webkit/tools/test_shell/SConscript +++ b/webkit/tools/test_shell/SConscript @@ -93,6 +93,7 @@ elif env['PLATFORM'] in ('posix', 'darwin'): ) input_files = [ + 'event_sending_controller.cc', 'layout_test_controller.cc', 'simple_resource_loader_bridge.cc', 'test_navigation_controller.cc', @@ -104,7 +105,6 @@ if env['PLATFORM'] == 'win32': input_files.extend([ 'drag_delegate.cc', 'drop_delegate.cc', - 'event_sending_controller.cc', 'test_shell.cc', 'test_shell_win.cc', 'test_webview_delegate.cc', diff --git a/webkit/tools/test_shell/event_sending_controller.cc b/webkit/tools/test_shell/event_sending_controller.cc index 12e69d3..ac10803 100644 --- a/webkit/tools/test_shell/event_sending_controller.cc +++ b/webkit/tools/test_shell/event_sending_controller.cc @@ -21,6 +21,7 @@ #endif #include <queue> +#include "base/logging.h" #include "base/ref_counted.h" #include "base/string_util.h" #include "base/time.h" @@ -400,6 +401,11 @@ int EventSendingController::GetButtonNumberFromSingleArg( } else if (L"delete" == code_str) { code = 0x33; } +#elif defined(OS_LINUX) + // TODO(agl): We obviously need to do something about keycodes here + if (true) { + NOTIMPLEMENTED(); + } #endif else { DCHECK(code_str.length() == 1); diff --git a/webkit/tools/test_shell/gtk/test_shell.cc b/webkit/tools/test_shell/gtk/test_shell.cc index c048f04..8a72e86 100644 --- a/webkit/tools/test_shell/gtk/test_shell.cc +++ b/webkit/tools/test_shell/gtk/test_shell.cc @@ -4,17 +4,24 @@ #include "webkit/tools/test_shell/test_shell.h" +#include <errno.h> +#include <fcntl.h> #include <gtk/gtk.h> +#include <unistd.h> +#include "base/file_path.h" #include "base/file_util.h" +#include "base/message_loop.h" #include "base/path_service.h" #include "base/string_util.h" #include "net/base/mime_util.h" +#include "webkit/glue/plugins/plugin_list.h" +#include "webkit/glue/resource_loader_bridge.h" +#include "webkit/glue/webdatasource.h" #include "webkit/glue/webframe.h" +#include "webkit/glue/webkit_glue.h" #include "webkit/glue/webpreferences.h" #include "webkit/glue/webview.h" -#include "webkit/glue/plugins/plugin_list.h" -#include "webkit/glue/resource_loader_bridge.h" #include "webkit/tools/test_shell/test_navigation_controller.h" #include "webkit/tools/test_shell/test_webview_delegate.h" @@ -25,6 +32,7 @@ WindowList* TestShell::window_list_; TestShell::TestShell() : delegate_(new TestWebViewDelegate(this)) { layout_test_controller_.reset(new LayoutTestController(this)); + event_sending_controller_.reset(new EventSendingController(this)); navigation_controller_.reset(new TestNavigationController(this)); } @@ -52,6 +60,44 @@ bool TestShell::CreateNewWindow(const std::wstring& startingURL, return true; } +void TestShell::ResetWebPreferences() { + DCHECK(web_prefs_); + + // Match the settings used by Mac DumpRenderTree. + if (web_prefs_) { + *web_prefs_ = WebPreferences(); + web_prefs_->standard_font_family = L"Times"; + web_prefs_->fixed_font_family = L"Courier"; + web_prefs_->serif_font_family = L"Times"; + web_prefs_->sans_serif_font_family = L"Helvetica"; + // These two fonts are picked from the intersection of + // Win XP font list and Vista font list : + // http://www.microsoft.com/typography/fonts/winxp.htm + // http://blogs.msdn.com/michkap/archive/2006/04/04/567881.aspx + // Some of them are installed only with CJK and complex script + // support enabled on Windows XP and are out of consideration here. + // (although we enabled both on our buildbots.) + // They (especially Impact for fantasy) are not typical cursive + // and fantasy fonts, but it should not matter for layout tests + // as long as they're available. + web_prefs_->cursive_font_family = L"Comic Sans MS"; + web_prefs_->fantasy_font_family = L"Impact"; + web_prefs_->default_encoding = L"ISO-8859-1"; + web_prefs_->default_font_size = 16; + web_prefs_->default_fixed_font_size = 13; + web_prefs_->minimum_font_size = 1; + web_prefs_->minimum_logical_font_size = 9; + web_prefs_->javascript_can_open_windows_automatically = true; + web_prefs_->dom_paste_enabled = true; + web_prefs_->developer_extras_enabled = interactive_; + web_prefs_->shrinks_standalone_images_to_fit = false; + web_prefs_->uses_universal_detector = false; + web_prefs_->text_areas_are_resizable = false; + web_prefs_->java_enabled = true; + web_prefs_->allow_scripts_to_close_windows = false; + } +} + bool TestShell::Initialize(const std::wstring& startingURL) { m_mainWnd = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(m_mainWnd), "Test Shell"); @@ -84,7 +130,7 @@ bool TestShell::Initialize(const std::wstring& startingURL) { -1 /* append */); gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); - m_webViewHost.reset(WebViewHost::Create(vbox, NULL, *TestShell::web_prefs_)); + m_webViewHost.reset(WebViewHost::Create(vbox, delegate_, *TestShell::web_prefs_)); if (!startingURL.empty()) LoadURL(startingURL.c_str()); @@ -99,6 +145,21 @@ void TestShell::TestFinished() { NOTIMPLEMENTED(); } +void TestShell::WaitTestFinished() { + DCHECK(!test_is_pending_) << "cannot be used recursively"; + + test_is_pending_ = true; + + // TODO(agl): Here windows forks a watchdog thread, but I'm punting on that + // for the moment. On POSIX systems we probably want to install a signal + // handler and use alarm(2). + + // TestFinished() will post a quit message to break this loop when the page + // finishes loading. + while (test_is_pending_) + MessageLoop::current()->Run(); +} + void TestShell::Show(WebView* webview, WindowOpenDisposition disposition) { delegate_->Show(webview, disposition); } @@ -126,6 +187,139 @@ WebWidget* TestShell::CreatePopupWidget(WebView* webview) { return NULL; } +void TestShell::ResizeSubViews() { + // The GTK functions to do this are deprecated because it's not really + // something that X windows supports. It's not clear exactly what should be + // done here. + NOTIMPLEMENTED(); +} + +/* static */ std::string TestShell::DumpImage( + WebFrame* web_frame, + const std::wstring& file_name) { + // Windows uses some platform specific bitmap functions here. + // TODO(agl): port + NOTIMPLEMENTED(); + return "00000000000000000000000000000000"; +} + +/* static */ void TestShell::DumpBackForwardList(std::wstring* result) { + result->clear(); + for (WindowList::iterator iter = TestShell::windowList()->begin(); + iter != TestShell::windowList()->end(); iter++) { + GtkWidget* window = *iter; + TestShell* shell = + static_cast<TestShell*>(g_object_get_data(G_OBJECT(window), "test-shell")); + webkit_glue::DumpBackForwardList(shell->webView(), NULL, result); + } +} + +/* static */ bool TestShell::RunFileTest(const char *filename, + const TestParams& params) { + // Load the test file into the first available window. + if (TestShell::windowList()->empty()) { + LOG(ERROR) << "No windows open."; + return false; + } + + GtkWidget* window = *(TestShell::windowList()->begin()); + TestShell* shell = + static_cast<TestShell*>(g_object_get_data(G_OBJECT(window), "test-shell")); + shell->ResetTestController(); + + // ResetTestController may have closed the window we were holding on to. + // Grab the first window again. + window = *(TestShell::windowList()->begin()); + shell = static_cast<TestShell*>(g_object_get_data(G_OBJECT(window), "test-shell")); + DCHECK(shell); + + // Clear focus between tests. + shell->m_focusedWidgetHost = NULL; + + // Make sure the previous load is stopped. + shell->webView()->StopLoading(); + shell->navigation_controller()->Reset(); + + // Clean up state between test runs. + webkit_glue::ResetBeforeTestRun(shell->webView()); + ResetWebPreferences(); + shell->webView()->SetPreferences(*web_prefs_); + + // TODO(agl): Maybe make the window hidden in the future. Window does this + // by positioning it off the screen but the GTK function to do this is + // deprecated and appears to have been removed. + + shell->ResizeSubViews(); + + if (strstr(filename, "loading/") || strstr(filename, "loading\\")) + shell->layout_test_controller()->SetShouldDumpFrameLoadCallbacks(true); + + shell->test_is_preparing_ = true; + + const std::wstring wstr = UTF8ToWide(filename); + shell->LoadURL(wstr.c_str()); + + shell->test_is_preparing_ = false; + shell->WaitTestFinished(); + + // Echo the url in the output so we know we're not getting out of sync. + printf("#URL:%s\n", filename); + + // Dump the requested representation. + WebFrame* webFrame = shell->webView()->GetMainFrame(); + if (webFrame) { + bool should_dump_as_text = + shell->layout_test_controller_->ShouldDumpAsText(); + bool dumped_anything = false; + if (params.dump_tree) { + dumped_anything = true; + // Text output: the test page can request different types of output + // which we handle here. + if (!should_dump_as_text) { + // Plain text pages should be dumped as text + std::wstring mime_type = webFrame->GetDataSource()->GetResponseMimeType(); + should_dump_as_text = (mime_type == L"text/plain"); + } + if (should_dump_as_text) { + bool recursive = shell->layout_test_controller_-> + ShouldDumpChildFramesAsText(); + std::string data_utf8 = WideToUTF8( + webkit_glue::DumpFramesAsText(webFrame, recursive)); + fwrite(data_utf8.c_str(), 1, data_utf8.size(), stdout); + } else { + printf("%s", WideToUTF8( + webkit_glue::DumpRenderer(webFrame)).c_str()); + + bool recursive = shell->layout_test_controller_-> + ShouldDumpChildFrameScrollPositions(); + printf("%s", WideToUTF8( + webkit_glue::DumpFrameScrollPosition(webFrame, recursive)). + c_str()); + } + + if (shell->layout_test_controller_->ShouldDumpBackForwardList()) { + std::wstring bfDump; + DumpBackForwardList(&bfDump); + printf("%s", WideToUTF8(bfDump).c_str()); + } + } + + if (params.dump_pixels && !should_dump_as_text) { + // Image output: we write the image data to the file given on the + // command line (for the dump pixels argument), and the MD5 sum to + // stdout. + dumped_anything = true; + std::string md5sum = DumpImage(webFrame, params.pixel_file_name); + printf("#MD5:%s\n", md5sum.c_str()); + } + if (dumped_anything) + printf("#EOF\n"); + fflush(stdout); + } + + return true; +} + void TestShell::LoadURL(const wchar_t* url) { LoadURLForFrame(url, NULL); @@ -140,6 +334,9 @@ void TestShell::LoadURLForFrame(const wchar_t* url, if (frame_name) frame_string = frame_name; + LOG(INFO) << "Loading " << WideToUTF8(url) << " in frame '" + << WideToUTF8(frame_string) << "'"; + navigation_controller_->LoadEntry(new TestNavigationEntry( -1, GURL(WideToUTF8(url)), std::wstring(), frame_string)); } @@ -187,13 +384,89 @@ void TestShell::GoBackOrForward(int offset) { navigation_controller_->GoToOffset(offset); } +static void WriteTextToFile(const std::wstring& data, + const FilePath& filepath) +{ + // This function does the same thing as the Windows version except that it + // takes a FilePath. We should be using WriteFile in base/file_util.h, but + // the patch to add the FilePath version of that file hasn't landed yet, so + // this is another TODO(agl) for the merging. + const int fd = open(filepath.value().c_str(), O_TRUNC | O_WRONLY | O_CREAT, 0600); + if (fd < 0) + return; + const std::string data_utf8 = WideToUTF8(data); + ssize_t n; + do { + n = write(fd, data_utf8.data(), data.size()); + } while (n == -1 && errno == EINTR); + close(fd); +} + + +std::wstring TestShell::GetDocumentText() +{ + return webkit_glue::DumpDocumentText(webView()->GetMainFrame()); +} + +// TODO(agl): +// This version of PromptForSaveFile uses FilePath, which is what the real +// version should be using. However, I don't want to step on tony's toes (as he +// is also editing this file), so this is a hack until we merge the files again. +// (There is also a PromptForSaveFile member in TestShell which returns a wstring) +static bool PromptForSaveFile(const char* prompt_title, + FilePath* result) +{ + char filenamebuffer[512]; + printf("Enter filename for \"%s\"\n", prompt_title); + fgets(filenamebuffer, sizeof(filenamebuffer), stdin); + *result = FilePath(filenamebuffer); + return true; +} + +void TestShell::DumpDocumentText() +{ + FilePath file_path; + if (!::PromptForSaveFile("Dump document text", &file_path)) + return; + + WriteTextToFile(webkit_glue::DumpDocumentText(webView()->GetMainFrame()), + file_path); +} + +void TestShell::DumpRenderTree() +{ + FilePath file_path; + if (!::PromptForSaveFile("Dump render tree", &file_path)) + return; + + WriteTextToFile(webkit_glue::DumpRenderer(webView()->GetMainFrame()), + file_path); +} + void TestShell::Reload() { navigation_controller_->Reload(); } std::string TestShell::RewriteLocalUrl(const std::string& url) { - NOTIMPLEMENTED(); - return ""; + // Convert file:///tmp/LayoutTests urls to the actual location on disk. + const char kPrefix[] = "file:///tmp/LayoutTests/"; + const int kPrefixLen = arraysize(kPrefix) - 1; + + std::string new_url(url); + if (url.compare(0, kPrefixLen, kPrefix, kPrefixLen) == 0) { + std::wstring replace_url; + PathService::Get(base::DIR_EXE, &replace_url); + file_util::UpOneDirectory(&replace_url); + file_util::UpOneDirectory(&replace_url); + file_util::AppendToPath(&replace_url, L"webkit"); + file_util::AppendToPath(&replace_url, L"data"); + file_util::AppendToPath(&replace_url, L"layout_tests"); + file_util::AppendToPath(&replace_url, L"LayoutTests"); + replace_url.push_back(file_util::kPathSeparator); + new_url = std::string("file:///") + + WideToUTF8(replace_url).append(url.substr(kPrefixLen)); + } + return new_url; } //----------------------------------------------------------------------------- diff --git a/webkit/tools/test_shell/gtk/test_webview_delegate.cc b/webkit/tools/test_shell/gtk/test_webview_delegate.cc index 739b459..92079a8 100644 --- a/webkit/tools/test_shell/gtk/test_webview_delegate.cc +++ b/webkit/tools/test_shell/gtk/test_webview_delegate.cc @@ -576,7 +576,9 @@ gfx::ViewHandle TestWebViewDelegate::GetContainingWindow(WebWidget* webwidget) { void TestWebViewDelegate::DidInvalidateRect(WebWidget* webwidget, const gfx::Rect& rect) { - NOTIMPLEMENTED(); + WebWidgetHost* host = GetHostForWidget(webwidget); + if (host) + host->DidInvalidateRect(rect); } void TestWebViewDelegate::DidScrollRect(WebWidget* webwidget, int dx, int dy, @@ -668,6 +670,7 @@ void TestWebViewDelegate::UpdateAddressBar(WebView* webView) { return; std::string frameURL = dataSource->GetRequest().GetMainDocumentURL().spec(); + LOG(INFO) << " -- Address bar " << frameURL; NOTIMPLEMENTED(); } diff --git a/webkit/tools/test_shell/gtk/webview_host.cc b/webkit/tools/test_shell/gtk/webview_host.cc index 4eecae1..e21099d 100644 --- a/webkit/tools/test_shell/gtk/webview_host.cc +++ b/webkit/tools/test_shell/gtk/webview_host.cc @@ -9,6 +9,7 @@ #include "base/gfx/platform_canvas.h" #include "base/gfx/rect.h" #include "base/gfx/size.h" +#include "base/logging.h" #include "webkit/glue/webinputevent.h" #include "webkit/glue/webview.h" @@ -16,13 +17,13 @@ WebViewHost* WebViewHost::Create(GtkWidget* box, WebViewDelegate* delegate, const WebPreferences& prefs) { - // TODO(agl): - // /usr/local/google/agl/src/chrome/src/webkit/tools/test_shell/gtk/webview_host.cc:19: error: no matching function for call to 'WebWidgetHost::Create(GtkWidget*&, WebViewDelegate*&)' - WebViewHost* host = reinterpret_cast<WebViewHost *>(WebWidgetHost::Create(box, NULL)); + WebViewHost* host = new WebViewHost(); + + LOG(INFO) << "In WebViewHost::Create"; + + host->view_ = WebWidgetHost::CreateWindow(box, host); + g_object_set_data(G_OBJECT(host->view_), "webwidgethost", host); - // TODO(erg): - // - Set "host->view_" - // - Call "host->webwidget_->Resize" host->webwidget_ = WebView::Create(delegate, prefs); host->webwidget_->Resize(gfx::Size(640, 480)); host->webwidget_->Layout(); diff --git a/webkit/tools/test_shell/gtk/webwidget_host.cc b/webkit/tools/test_shell/gtk/webwidget_host.cc index 847a0a7..edc0a92 100644 --- a/webkit/tools/test_shell/gtk/webwidget_host.cc +++ b/webkit/tools/test_shell/gtk/webwidget_host.cc @@ -66,30 +66,47 @@ gboolean FocusOut(GtkWidget* widget, GdkEventFocus* focus, gpointer userdata) { // ----------------------------------------------------------------------------- +gfx::WindowHandle WebWidgetHost::CreateWindow(gfx::WindowHandle box, + void* host) { + GtkWidget* widget = gtk_drawing_area_new(); + gtk_box_pack_start(GTK_BOX(box), widget, TRUE, TRUE, 0); + + gtk_widget_add_events(widget, GDK_EXPOSURE_MASK | + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK); + // TODO(agl): set GTK_CAN_FOCUS flag + g_signal_connect(widget, "configure-event", G_CALLBACK(ConfigureEvent), host); + g_signal_connect(widget, "expose-event", G_CALLBACK(ExposeEvent), host); + g_signal_connect(widget, "destroy-event", G_CALLBACK(DestroyEvent), host); + g_signal_connect(widget, "key-press-event", G_CALLBACK(KeyPressEvent), host); + g_signal_connect(widget, "focus-in-event", G_CALLBACK(FocusIn), host); + g_signal_connect(widget, "focus-out-event", G_CALLBACK(FocusOut), host); + + return widget; +} + WebWidgetHost* WebWidgetHost::Create(gfx::WindowHandle box, WebWidgetDelegate* delegate) { + LOG(INFO) << "In WebWidgetHost::Create"; + WebWidgetHost* host = new WebWidgetHost(); - host->view_ = gtk_drawing_area_new(); - gtk_widget_add_events(host->view_, GDK_EXPOSURE_MASK | - GDK_POINTER_MOTION_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_KEY_PRESS_MASK | - GDK_KEY_RELEASE_MASK); - // TODO(agl): set GTK_CAN_FOCUS flag + host->view_ = CreateWindow(box, host); host->webwidget_ = WebWidget::Create(delegate); - g_object_set_data(G_OBJECT(host->view_), "webwidgethost", host); - g_signal_connect(host->view_, "configure-event", G_CALLBACK(ConfigureEvent), host); - g_signal_connect(host->view_, "expose-event", G_CALLBACK(ExposeEvent), host); - g_signal_connect(host->view_, "destroy-event", G_CALLBACK(DestroyEvent), host); - g_signal_connect(host->view_, "key-press-event", G_CALLBACK(KeyPressEvent), host); - g_signal_connect(host->view_, "focus-in-event", G_CALLBACK(FocusIn), host); - g_signal_connect(host->view_, "focus-out-event", G_CALLBACK(FocusOut), host); + return host; +} - gtk_box_pack_start(GTK_BOX(box), host->view_, TRUE, TRUE, 0); +void WebWidgetHost::DidInvalidateRect(const gfx::Rect& rect) { + LOG(INFO) << " -- Invalidate " << rect.x() << " " + << rect.y() << " " + << rect.width() << " " + << rect.height() << " "; - return host; + gtk_widget_queue_draw_area(GTK_WIDGET(view_), rect.x(), rect.y(), rect.width(), + rect.height()); } void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { @@ -167,7 +184,6 @@ void WebWidgetHost::Paint() { LOG(INFO) << "Using pixel data at " << (void *) gdk_pixbuf_get_pixels(bitdev->pixbuf()); gdk_draw_pixbuf(view_->window, NULL, bitdev->pixbuf(), 0, 0, 0, 0, width, height, GDK_RGB_DITHER_NONE, 0, 0); - gdk_pixbuf_save(bitdev->pixbuf(), "output.png", "png", NULL, NULL); } void WebWidgetHost::PaintRect(const gfx::Rect& rect) { diff --git a/webkit/tools/test_shell/test_shell_main_gtk.cc b/webkit/tools/test_shell/test_shell_main_gtk.cc index 48cf47a..57b94ed 100644 --- a/webkit/tools/test_shell/test_shell_main_gtk.cc +++ b/webkit/tools/test_shell/test_shell_main_gtk.cc @@ -46,6 +46,9 @@ int main(int argc, char* argv[]) { file_util::AppendToPath(&uri, L"data"); file_util::AppendToPath(&uri, L"test_shell"); file_util::AppendToPath(&uri, L"index.html"); + // For now, loading from disk doesn't work so we set the URI to the + // homepage. + uri = L"http://www.google.com"; } if (parsed_command_line.GetLooseValueCount() > 0) { diff --git a/webkit/tools/test_shell/webwidget_host.h b/webkit/tools/test_shell/webwidget_host.h index eb52bc0..583c9bf 100644 --- a/webkit/tools/test_shell/webwidget_host.h +++ b/webkit/tools/test_shell/webwidget_host.h @@ -71,6 +71,16 @@ class WebWidgetHost { protected: #elif defined(OS_LINUX) public: + // --------------------------------------------------------------------------- + // This is needed on Linux because the GtkWidget creation is the same between + // both web view hosts and web widget hosts. The Windows code manages this by + // reusing the WndProc function (static, above). However, GTK doesn't use a + // single big callback function like that so we have a static function that + // sets up a GtkWidget correctly. + // parent: a GtkBox to pack the new widget at the end of + // host: a pointer to a WebWidgetHost (or subclass thereof) + // --------------------------------------------------------------------------- + static gfx::WindowHandle CreateWindow(gfx::WindowHandle parent, void* host); void WindowDestroyed(); void Resize(const gfx::Size& size); #endif |